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 + -
显示快捷键?