nettimedlg.cpp
来自「个人计算机定时自动执行软件,是 一个值得看看的程序。」· C++ 代码 · 共 395 行
CPP
395 行
// NetTimeDlg.cpp : implementation file
//
#include "stdafx.h"
#include "clock.h"
#include "NetTimeDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CNetTimeDlg dialog
#include<wininet.h>
#pragma comment(lib,"wininet.lib")//该指令将一个注释记录放入一个对象文件或可执行文件中。
//常用的lib关键字,可以帮我们连入一个库文件。
#include <winsock2.h>
#pragma comment(lib, "wsock32")
#define WM_SOCKET_NOTIFY (WM_USER + 11)
char m_szIPAddr[32]={0};
SOCKET m_sock;
ULONG m_ulTime ;
CNetTimeDlg::CNetTimeDlg(CWnd* pParent /*=NULL*/)
: CDialog(CNetTimeDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CNetTimeDlg)
m_inphase = TRUE;
m_unable = TRUE;
//}}AFX_DATA_INIT
}
void CNetTimeDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CNetTimeDlg)
DDX_Control(pDX, IDC_NETTIME_SERVER, m_cc);
DDX_Control(pDX, IDC_NETTIME_EDIT, m_edit);
DDX_Check(pDX, IDC_NETTIME_INPHASE, m_inphase);
DDX_Check(pDX, IDC_NETTIME_UNABLE, m_unable);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CNetTimeDlg, CDialog)
//{{AFX_MSG_MAP(CNetTimeDlg)
ON_BN_CLICKED(IDC_NETTIME_BUTTON, OnNettimeButton)
ON_BN_CLICKED(IDC_NETTIME_UNABLE, OnNettimeUnable)
ON_BN_CLICKED(IDC_NETTIME_INPHASE, OnNettimeInphase)
ON_CBN_SELCHANGE(IDC_NETTIME_SERVER, OnSelchangeNettimeServer)
ON_WM_TIMER()
ON_MESSAGE(WM_SOCKET_NOTIFY,OnSocketNotify)
ON_WM_SHOWWINDOW()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CNetTimeDlg message handlers
BOOL CNetTimeDlg::OnInitDialog()
{
CDialog::OnInitDialog();
char files_path[512];
GetModuleFileName(NULL,files_path,512);
char *pp = strrchr(files_path,'\\');
pp[1]='\0';
strcat(files_path,"Settings.ini");
// m_checktimeover=FALSE;
m_unable = GetPrivateProfileInt("NetTime","m_unable",1,files_path);
m_inphase = GetPrivateProfileInt("NetTime","m_inphase",1,files_path);
DWORD dwFlag;
if(!InternetGetConnectedState(&dwFlag, 0))//得到系统网络联接的状态
{
SetDlgItemText(IDC_NETTIME_EDIT,"\r\n\r\n\t本机网络未连接...");
m_cc.EnableWindow(false);
GetDlgItem(IDC_NETTIME_BUTTON)->EnableWindow(false);
}
else if(dwFlag & INTERNET_CONNECTION_MODEM)
{
SetDlgItemText(IDC_NETTIME_EDIT,"\r\n\r\n\t采用调治解调器上网...");
}
else if(dwFlag & INTERNET_CONNECTION_LAN)
{
SetDlgItemText(IDC_NETTIME_EDIT,"\r\n\r\n\t采用网卡通过局域网上网...");
}
else if(dwFlag & INTERNET_CONNECTION_PROXY)
{
SetDlgItemText(IDC_NETTIME_EDIT,"\r\n\r\n\t使用代理服务器上网...");
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CNetTimeDlg::OnNettimeUnable() //当与时间服务器无法连接,或同步失败时提示
{
if(m_unable)
{
m_unable=0;
}
else
{
m_unable=1;
}
}
void CNetTimeDlg::OnNettimeInphase() //同步完成显示提示信息
{
if(m_inphase)
{
m_inphase=0;
}
else
{
m_inphase=1;
}
}
void CNetTimeDlg::OnNettimeButton() //立刻对时
{
SetDlgItemText(IDC_NETTIME_EDIT,"");
m_cc.GetLBText(m_cc.GetCurSel(),m_szIPAddr);
WSADATA WSAData;
// Call "WSAStartup"显示szDescription字串,并简要提供了一些版本资讯。
//MAKEWORD(2,0)将第一个参数设定为0x0200(表示2.0版本)
::WSAStartup (MAKEWORD(2,0), &WSAData);
EditPrintf ("Started up %hs\r\n", WSAData.szDescription);
/* 第一个参数AF_INET是一个位址种类,表示此处是某种Internet位址。
第二个参数表示资料以资料流的形式传回,而不是以资料封包的形式传回
(我们需要的资料只有4个位元组长,而资料封包适用于较大的资料块)。
最后一个参数是一个协定,我们指定使用的Internet协定是TCP。它是RFC-868所定义的两个协定之一。
socket函数的传回值储存在SOCKET型态的变数中,以便后面的Socket函数的调用。 */
m_sock = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
if (m_sock == INVALID_SOCKET)
{
EditPrintf ( "Socket 创建失败! #%i\r\n", ::WSAGetLastError ()) ;
::WSACleanup () ;
return ;
}
EditPrintf ("Socket %i 已经成功创建!\r\n", m_sock) ;
/* Call "WSAAsyncSelect"
WSAAsynchSelect是另一个Windows特有的Socket函数。此函数用于避免因Internet回应过慢而造成应用程序当住。
在WinSock文件中,有些函数与「阻碍性(blocking)」有关。也就是说,它们不能保证立即把控制项权传回给程序。
WSAAsyncSelect函数强制阻碍性的函数转为非阻碍性的,即在函数执行完之前把控制项传回给程序。
函数的结果以讯息的形式报告给应用程序。WSAAsyncSelect函数让应用程序指定讯息和接收讯息的视窗的数值。
使用程序定义的一个讯息,该讯息称为WM_SOCKET_NOTIFY,最后一个参数来指定讯息发送的条件,
特别在连结和接收资料时(FD_CONNECT | FD_READ) */
if (SOCKET_ERROR== ::WSAAsyncSelect(m_sock,GetSafeHwnd(),WM_SOCKET_NOTIFY,FD_CONNECT|FD_READ))
{
EditPrintf ("WSAAsyncSelect 错误: #%i.\r\n",::WSAGetLastError ());
::closesocket (m_sock);
::WSACleanup ();
return;
}
/* Call "connect" with IP address and time-server port
将sin_port设定为埠号,这里是时间协定的埠号,RFC-868显示为37。
但不要像我最初时那样,将此栏位设为37。当大多数数字通过Internet时,
结构的这个埠号栏位必须是「big endian」的,即最高的位元组排第一个。
Intel微处理器是little endian。幸运的是,htons(「host-to-network short」)函数使位元组翻转
用inet_addr函数将储存在m_szIPAddr字串中的伺服器位址转化为无正负号长整数 */
static struct sockaddr_in sa ;
sa.sin_family = AF_INET ; //#define AF_INET 2 (internetwork: UDP, TCP, etc.)
sa.sin_port = ::htons (IPPORT_TIMESERVER) ; //#define IPPORT_TIMESERVER 37
sa.sin_addr.S_un.S_addr = ::inet_addr (m_szIPAddr) ;
::connect(m_sock, (SOCKADDR*) &sa, sizeof(sa)) ;
/* connect函数通常已经会阻碍著后面程序的执行,这是因为连结成功以前需要花些时间。
然而,由于调用了WSAAsyncSelect,所以connect不会等待连结,事实上,它会立即传回SOCKET_ERROR的值。
并不是出现了错误,这只是表示现在还没有连线成功而已。NETTIME也不会检查这个传回值,只是调用::WSAGetLastError而已。
如果::WSAGetLastError传回WSAEWOULDBLOCK(即函数的执行通常要受阻,但这里并没有受阻),那就一切都还很正常。 */
if (WSAEWOULDBLOCK != ::WSAGetLastError ())
{
EditPrintf ("连接错误: #%i.\r\n", ::WSAGetLastError ()) ;
::closesocket (m_sock) ;
::WSACleanup () ;
return;
}
EditPrintf ( "正在连接: %hs...", m_szIPAddr) ;
/* 连结最终完成时,主对话框由WM_SOCKET_NOTIFY消息-NETTIME在WSAAsyncSelect函数中指定的程序自订讯息所通知。
lParam的低字组等于FD_CONNECT,高字组表示错误。这时的错误可能是程序不能连结到指定的伺服器。
NETTIME还列出了其他伺服器,供您选择,让您可以试试其他的伺服器。
如果一切顺利,那么NETTIME将调用recv(「receive:接收」)函数来读取资料.
设定了一个计时器,只是在程序视窗中显示句点,以指示程序正在执行。*/
SetTimer (1, 1000, NULL);
// m_checktimeover=TRUE;
}
void CNetTimeDlg::OnSocketNotify(WPARAM wp,LPARAM lp)
{
WORD wEvent = WSAGETSELECTEVENT (lp) ; // ie, LOWORD
WORD wError = WSAGETSELECTERROR (lp) ; // ie, HIWORD
//处理WSAAsyncSelect中指定得两种事件 FD_CONNECT | FD_READ
switch (wEvent)
{
case FD_CONNECT:
EditPrintf ("\r\n");
if (wError)
{
EditPrintf ( "连接错误: #%i.", wError);
if(m_unable)//如果"无法联接,或同步失败提示"打勾,则在这里显示信息
{
AfxMessageBox("网络无法联接!",MB_ICONSTOP);
}
return ;
}//
EditPrintf ( "已成功连接到时间服务器: %hs.\r\n", m_szIPAddr) ;
/* 调用recv(「receive:接收」)函数来读取资料.
该调用将产生WM_SOCKET_NOTIFY讯息,这时带有FD_READ的事件代码。产生一个WSAEWOULDBLOCK错误以表示函数通常受阻,
但这时没有受阻。理论上来说(当然这不大可能),函数至少能传回资料的一部分,然后透过再次调用以获得其余的32个位元组值。
(char *) &m_ulTime, 4意味著,用4个位元组来储存m_ulTime变数
最后一个参数MSG_PEEK表示只是读此资料,并不将其从输入伫列中删除WM_SOCKET_NOTIFY讯息 */
::recv (m_sock, (char *) &m_ulTime, 4, MSG_PEEK);
EditPrintf ( "正在接受数据,请稍候...");
return ;
case FD_READ:
KillTimer (1);
EditPrintf ( "\r\n");
if (wError)
{
EditPrintf ( "FD_READ 错误! #%i.", wError);
if(m_unable)//如果"无法联接,或同步失败提示"打勾,则在这里显示信息
{
AfxMessageBox("网络同步失败!",MB_ICONSTOP);
}
return ;
}
/* 获得其余的32个位元值,这时最后的参数是0,用于从伫列中删除WM_SOCKET_NOTIFY讯息。
接收的32位元的m_ulTime值是从1990年1月1日开始的0:00 UTC秒数
但最高顺序的位元组是第一个位元组,因此该值必须通过ntohl(「network-to-host long」)函数处理来调整位元组顺序,
以便Intel微处理器能够处理。然后调用ChangeSystemTime函数。 */
::recv(m_sock, (char *) &m_ulTime, 4, 0) ;
m_ulTime = ::ntohl (m_ulTime) ;
EditPrintf ( "接受的时间为从1900年1月1日起 %u 秒 \r\n", m_ulTime) ;
if(m_inphase)//如果"同步完成提示信息"打勾,则在这里显示信息
{
AfxMessageBox("同步完成!",MB_ICONINFORMATION);
}
}
this->ChangeSystemTime() ;
}
void CNetTimeDlg::ChangeSystemTime()
{
FILETIME ftNew ;
SYSTEMTIME stOld, stNew ;
::GetLocalTime (&stOld) ; //首先取得目前的本地时间
stNew.wYear = 1900 ;
stNew.wMonth = 1 ;
stNew.wDay = 1 ;
stNew.wHour = 0 ;
stNew.wMinute = 0 ;
stNew.wSecond = 0 ;
stNew.wMilliseconds = 0 ;
::SystemTimeToFileTime (&stNew, &ftNew);
/* 将SYSTEMTIME结构设定为1900年1月1日午夜(0时)。
并将这个SYSTEMTIME结构传递给SystemTimeToFileTime,将此结构转化为FILETIME结构。
FILETIME实际上只是由两个32位元的DWORD一起组成64位元的整数,
用来表示从1601年1月1日至今间隔为100奈秒(nanosecond)的间隔数。 */
LARGE_INTEGER li ; //64位大整数
li = * (LARGE_INTEGER *) &ftNew;
li.QuadPart += (LONGLONG) 10000000 * m_ulTime;
ftNew = * (FILETIME *) &li;
::FileTimeToSystemTime (&ftNew, &stNew);
if (::SetSystemTime (&stNew)) //调用SetSystemTime来设定时间
{
::GetLocalTime (&stNew);
//最初的本地时间和新的本地时间一起传递给FormatUpdatedTime
//这个函数用::GetTimeFormat函数和::GetDateFormat函数将时间转化为ASCII字串。
this->FormatUpdatedTime (&stOld, &stNew);
}
else
EditPrintf ("不能设置新的日期和时间!");
}
void CNetTimeDlg::FormatUpdatedTime(SYSTEMTIME *pstOld, SYSTEMTIME *pstNew)
{
TCHAR szDateOld [64], szTimeOld [64], szDateNew [64], szTimeNew [64] ;
::GetDateFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | DATE_SHORTDATE,
pstOld, NULL, szDateOld, sizeof (szDateOld)) ;
::GetTimeFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE |TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
pstOld, NULL, szTimeOld, sizeof (szTimeOld)) ;
::GetDateFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE | DATE_SHORTDATE,
pstNew, NULL, szDateNew, sizeof (szDateNew)) ;
::GetTimeFormat (LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE |TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT,
pstNew, NULL, szTimeNew, sizeof (szTimeNew)) ;
EditPrintf ( "\r\n系统的日期和时间已成功更改:"
"\r\n以前\t%s, %s.%03i \r\n现在\t%s, %s.%03i.",
szDateOld, szTimeOld, pstOld->wMilliseconds,
szDateNew, szTimeNew, pstNew->wMilliseconds) ;
}
void CNetTimeDlg::EditPrintf(TCHAR *szFormat, ...)
{
TCHAR szBuffer [1024];
va_list pArgList; //typedef char * va_list;
va_start (pArgList, szFormat);
::wvsprintf (szBuffer, szFormat, pArgList);
va_end (pArgList);
m_edit.SetSel(-1,-1); //将插入光标放于最后
m_edit.ReplaceSel(szBuffer);
m_edit.ScrollWindow(0,0); //滚动到插入点
}
void CNetTimeDlg::OnTimer(UINT nIDEvent)
{
EditPrintf(".");
CDialog::OnTimer(nIDEvent);
}
void CNetTimeDlg::OnSelchangeNettimeServer() //选择改变时
{
m_cc.GetLBText(m_cc.GetCurSel(),m_szIPAddr);
// MessageBox(m_szIPAddr);
}
void CNetTimeDlg::OnOK() //确定
{
char files_path[512];
GetModuleFileName(NULL,files_path,512);
char *pp = strrchr(files_path,'\\');
pp[1]='\0';
strcat(files_path,"Settings.ini");
WritePrivateProfileString("NetTime","m_inphase",m_inphase? "1":"0",files_path);
WritePrivateProfileString("NetTime","m_unable",m_unable? "1":"0",files_path);
//按确定才改变系统时间
// if(m_checktimeover)//是否对时,如果对时完成,才改变系统时间
// {
// this->ChangeSystemTime() ;
// }
CDialog::OnOK();
}
void CNetTimeDlg::OnShowWindow(BOOL bShow, UINT nStatus)
{
CDialog::OnShowWindow(bShow, nStatus);
CRect rect;
GetWindowRect(&rect);
SetWindowPos(NULL,250,200,rect.Width(),rect.Height(),0);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?