Dowload all the source, including the VC 6.0 project files and icons.
AtomicTime.cpp (version 2.2)
// AtomicTime.cpp
// Version 2.1.0 February 4, 1997
// Version 2.2.0 January 1, 2002
// Freeware, 1997 Tom Wuttke http://schmail.com/atomictime
//
// If you think this program is cool, or used it to learn winsock
// programming, let me know.
//
// Compiled with VC++5.0 and 6.0. Other compilers might
// have problems with the LONGLONG (__int64) data type.
//
// Files:
// AtomicTime.cpp - source file
// AtomicTime.rc - resource file
// resource.h - resource include file
// AtomTim1.ico - icon1
// AtomTim2.ico - icon2
// AtomTim3.ico - icon3
// AtomicTime.dsp - VC++6.0 project
//
// This part taken from http://www.nopcode.com/AggressiveOptimize.shtml
// Adding this stuff makes the release exe file smaller
#ifndef _DEBUG
// /Og (global optimizations), /Os (favor small code), /Oy (no frame pointers)
#pragma optimize("gsy",on)
#pragma comment(linker,"/RELEASE")
//#pragma comment(linker,"/merge:.rdata=.data") // makes code larger in this case
#pragma comment(linker,"/merge:.text=.data")
#pragma comment(linker,"/merge:.reloc=.data")
#pragma comment(linker,"/ignore:4078")
#if _MSC_VER >= 1000
#pragma comment(linker,"/opt:nowin98")
#endif // _MSC_VER >= 1000
#endif // _DEBUG
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>
#include <shellapi.h>
#include <winsock.h>
#include "resource.h"
// Version for the registry upgrade thing
// (More version stuff is in the rc file)
const DWORD cVersion = 0x20003;
#ifdef _DEBUG
#include <stdarg.h>
#include <stdio.h>
#define cWindowName "Atomic Time v2.2 debug"
#else
#define cWindowName "Atomic Time v2.2"
#endif
#define cUninstallString "SOFTWARE\\Microsoft\\Windows\\Current" \
"Version\\Uninstall\\AtomicTime"
const cMaxIter = 7; // Number of times to sample the remote server
const cUsedIter = 4; // Number of samples to actually use
const cMaxSockets = 8; // Max simultaneous socket connections
// Each server has its last used status, which is remembered between
// sessions
//
enum
{
eStatusNothing,
eStatusNetError,
eStatusNotResponding,
eStatusSlowResponding,
eStatusRefused,
eStatusNetDown,
eStatusNoDNSEntry,
eStatusDisabled,
eStatusInterrupted,
eStatusThrownOut = 100,
eStatusTest,
eStatusUsed // This is the good status
};
// The state of the server's socket connection
enum
{
eProgressNot,
eProgressWaiting,
eProgressLookup,
eProgressConnecting,
eProgressReading
};
// Which of the three global messages to display
enum
{
eMessageIdle,
eMessageChecking,
eMessageTesting
};
const int cMaxServers = 64;
typedef char ServerName[128];
// This is the registry entry for each server. It is saved
// between sessions
struct ServerItem
{
ServerName sName; // 127 character name of server
int enabled, // true if enabled (has icon)
status; // see enum above
DWORD latency; // last average latency from reading
LONGLONG correction; // last average correction
};
// This is the temporary info about a server. It is NOT saved
// between sessions
struct ServerState
{
bool dirty; // true if progress has changed since last display
int progress, // see enum above
iter; // which connection is in progress (0 <= iter <= cMaxIter)
DWORD ip, // ip address of this server
latency[cMaxIter]; // latencies for each connection
LONGLONG correction[cMaxIter]; // corrections for each connection
SOCKET s; // socket handle during asynchronous calls
HANDLE h; // Asynchronous handle for DNS calls
char hostentBuf[MAXGETHOSTSTRUCT]; // buffer for DNS request
LONGLONG timeoutTime; // determines last socket activity, used for timing out
};
HINSTANCE g_hInstance;
HWND g_hDlg = 0, // handle of main window
g_hwndList; // handle of server list view control
// All times are in milliseconds.
// All dates are in milliseconds since 1900.
LONGLONG g_LocalTimeOffset = 0, // user's local time offset
g_LastCorrectionTime = 0, // date of last system time change
g_LastAttemptTime = 0, // date of last attempt to corrrect system time
g_LastCorrectionValue = 0, // value of last system time change
g_CorrectionInterval, // how long to wait between auto-corrections
g_timeoutTime = 0; // determines last socket activity, used for timing out
char g_mode = 0; // command line argument, 's' or 'u'
int g_nSchedule = 0, // Slider value for scheduled correction, 0-9
g_nRunningServerCount = 0, // How many servers are reqeusted
g_nReallyRunningServerCount = 0, // How many socket connections are open
g_nSortColumn = 1, // Which column we are sorting the server list by
g_nSortPolarity = -1; // Are we sorting up or down?
bool g_bAutoQuit = false, // quit after ten seconds of idle
g_bDefaultServers = true, // use a default list of servers
g_bTesting, // the server connections are testing, not correcting
g_bFirstServerList = false, // the user is using the original servers
g_bWinsock = false, // Is winsock loaded?
g_bCorrectOnLaunch = false, // Should we just correct and not show UI?
g_bProblem = false; // Was there a connection problem?
ServerItem g_ServerItems[cMaxServers];
ServerState g_ServerStates[cMaxServers];
void ServerStart(int i);
void ServerDNS(HANDLE h, WORD error);
void ServerGotIP(int i);
void ServerConnected(int i, int err);
void ServerRead(int i, int err);
void ServerTimeAdjust(int i);
void ServerStop(int i, int status);
bool DoSystemTimeCorrection();
void Keep4BestServers();
void ChangeMessage(int nMessageIndex);
void SetTrackCaption();
int CALLBACK ServerListCompareFunc(LPARAM lParam1, LPARAM lParam2,
LPARAM);
void AbortAllServers(int status);
void Cleanup();
void Fatal(char*s)
{
static bFatal = false;
if (!bFatal)
{
bFatal = true;
if (g_hDlg)
{
Cleanup();
}
MessageBox(g_hDlg, s, cWindowName, MB_ICONERROR | MB_SYSTEMMODAL);
if (g_bWinsock)
{
WSACleanup();
}
ExitProcess(0);
}
}
#ifdef _DEBUG
#define dprintf(a) _dprintf a
void _dprintf(const char* format, ...)
{
va_list args;
char s[1000];
DWORD temp;
HANDLE h = CreateFile("AtomicTimeLog.txt", GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_WRITE_THROUGH, NULL);
va_start(args, format);
vsprintf(s, format, args);
va_end(args);
if (h != INVALID_HANDLE_VALUE)
{
SetFilePointer(h, 0, 0, FILE_END);
WriteFile(h, s, lstrlen(s), &temp, NULL);
CloseHandle(h);
}
}
#else
#define dprintf(a) ((void)0)
#endif
// REGISTRY FUNCTIONS
// ---------------------------------------------------------------------------
// Add or remove ourselves to the registry's secret auto-start area
void SetRunOnStart(BOOL state)
{
HKEY hkey;
char s[MAX_PATH + 2];
int len;
GetModuleFileName(NULL, s, sizeof(s) - 2);
lstrcat(s, " s");
len = lstrlen(s) + 1;
if (!RegOpenKey(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
&hkey))
{
if (state)
{
RegSetValueEx(hkey, "AtomicTime", 0, REG_SZ, (PBYTE)s, len);
}
else
{
RegDeleteValue(hkey, "AtomicTime");
}
RegCloseKey(hkey);
}
// Add the uninstall reg entry as long as we're here
if (RegOpenKey(HKEY_LOCAL_MACHINE, cUninstallString, &hkey))
{
if (RegCreateKey(HKEY_LOCAL_MACHINE, cUninstallString, &hkey))
{
return;
}
}
s[len - 2] = 'u';
RegSetValueEx(hkey, "DisplayName", 0, REG_SZ, (PBYTE)"AtomicTime", 11);
RegSetValueEx(hkey, "UninstallString", 0, REG_SZ, (PBYTE)s, len);
RegCloseKey(hkey);
}
// Add or remove a registry key to our area
void SetReg(LPCTSTR value, void *data = NULL, DWORD size = 0)
{
HKEY hKey;
if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\AtomicTime", &hKey))
{
if (RegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\AtomicTime", &hKey))
{
Fatal("Registry Problem");
return;
}
}
if (!data || !size)
{
DWORD error = RegDeleteValue(hKey, value);
if (error && error != ERROR_FILE_NOT_FOUND)
{
Fatal("Registry Problem");
return;
}
}
else if (RegSetValueEx(hKey, value, 0, (size == 4) ? REG_DWORD:REG_BINARY,
(PBYTE)data, size))
{
Fatal("Registry Problem");
return;
}
RegCloseKey(hKey);
}
// Get a registry key from our area
// returns true on success.
bool GetReg(LPCTSTR value = NULL, void* data = NULL)
{
HKEY hKey;
bool result = false;
DWORD size;
if (!RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\AtomicTime", &hKey))
{
if (data && value)
{
if (!RegQueryValueEx(hKey, value, 0, NULL, NULL, &size) &&
!RegQueryValueEx(hKey, value, 0, NULL, (PBYTE)data, &size))
{
result = true;
}
}
else
{
result = true;
}
RegCloseKey(hKey);
}
return result;
}
// TIME FUNCTIONS
// ---------------------------------------------------------------------------
LONGLONG GetMillisecondsSince1900()
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return *((LONGLONG *)&ft) / 10000 - LONGLONG(9435484800000);
}
LONGLONG OffsetMillisecondsSince1900(LONGLONG correction)
{
FILETIME ft;
SYSTEMTIME st;
LONGLONG time;
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
time = GetMillisecondsSince1900() + correction;
*((LONGLONG *)&ft) = 10000 * (time + LONGLONG(9435484800000));
FileTimeToSystemTime(&ft, &st);
if (!SetSystemTime(&st))
{
time = 0;
}
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
if (!time && GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
{
Fatal("Cannot set the system time");
}
return time;
}
void FormatMillisecondsSince1900(DWORDLONG time, char* s)
{
FILETIME ft, ftLocal;
SYSTEMTIME st;
int hour;
*((DWORDLONG *)&ft) = 10000 * (time + DWORDLONG(9435484800000));
FileTimeToLocalFileTime(&ft, &ftLocal);
FileTimeToSystemTime(&ftLocal, &st);
hour = st.wHour;
if (hour > 12)
{
hour -= 12;
}
else if (!hour)
{
hour = 12;
}
wsprintf(s, "%u:%02u:%02u%cm on %u/%u/%02u ",
hour, st.wMinute, st.wSecond, (st.wHour >= 12) ? 'p':'a',
st.wMonth, st.wDay, st.wYear % 100);
}
// Similar to GetTickCount and timeGetTime, but more accurate and faster
DWORD GetMilliseconds()
{
static DWORDLONG perfScale = 0;
DWORDLONG perf;
if (!perfScale)
{
QueryPerformanceFrequency((LARGE_INTEGER*)&perfScale);
perfScale = (DWORDLONG(1) << 32) * 1000 / perfScale;
}
QueryPerformanceCounter((LARGE_INTEGER*)&perf);
return DWORD((perf * perfScale) >> 32);
}
void MillisecondsToText(LONGLONG ms, char* s)
{
LONGLONG base, unityBase = 10;
char s2[2];
char cUnits;
if (ms >= 0)
{
lstrcpy(s, "+");
}
else if (ms < 0)
{
ms = -ms;
lstrcpy(s, "-");
}
if (ms < 1000 * 60 * 2)
{
cUnits = 's';
ms /= 10;
unityBase = 10;
}
else if (ms < 1000 * 60 * 60 * 2)
{
cUnits = 'm';
ms /= 60;
unityBase = 100;
}
else if (ms < 1000 * 60 * 60 * 24 * 2)
{
cUnits = 'h';
ms /= 60 * 60;
unityBase = 100;
}
else
{
cUnits = 'd';
ms /= 60 * 60 * 24;
unityBase = 100;
}
ms += 5;
ms /= 10;
if (!ms)
{
*s = ' ';
}
base = unityBase * 10;
while (base <= ms) base *= 10;
s2[1] = 0;
while (base > 1)
{
base /= 10;
s2[0] = (char)(ms / base);
ms -= s2[0] * base;
s2[0] += '0';
lstrcat(s, s2);
if (base == unityBase)
lstrcat(s, ".");
}
s2[0] = cUnits;
lstrcat(s, s2);
}
LONGLONG TextToMilliseconds(char* s)
{
LONGLONG result = 0;
int bias = 1000, sign = 1;
BOOL decFound = FALSE;
char c;
while ((c = *s++) != 0)
{
if (bias > 1 && c >= '0' && c <= '9')
{
result = result * 10 + (c - '0');
if (decFound)
{
bias /= 10;
}
}
else if (c == '.')
{
decFound = TRUE;
}
else if (c == '-')
{
sign = -sign;
}
else if (c == 'h' || c == 'H')
{
result *= 3600;
}
else if (c == 'm' || c == 'M')
{
result *= 60;
}
else if (c == 'd' || c == 'D')
{
result *= 3600 * 24;
}
}
return result * (bias * sign);
}
// SERVER DISPLAY AND SAVE/LOAD FUNCTIONS
// ---------------------------------------------------------------------------
// Adds, removes, or updates a server's entry in the list view
//
// If bJustProgress is true, assume this is just an update of
// the server's progress, and not a complete change
void DisplayServer(int i, bool bJustProgress = false)
{
LV_ITEM lvi;
LV_FINDINFO lfi;
int iFound;
lvi.iSubItem = 0;
lfi.flags = LVFI_PARAM;
for (i = 0; i < cMaxServers; i++)
{
ServerItem& si = g_ServerItems[i];
ServerState& ss = g_ServerStates[i];
char sLatency[100], sCorrection[100];
lfi.lParam = i;
ss.dirty = false;
if (bJustProgress && !si.sName[0])
{
continue;
}
ReFind:
iFound = ListView_FindItem(g_hwndList, -1, &lfi);
if (si.sName[0])
{
if (!bJustProgress)
{
if (iFound < 0)
{
iFound = ListView_GetItemCount(g_hwndList);
lvi.lParam = i;
lvi.iItem = iFound;
lvi.mask = LVIF_PARAM;
ListView_InsertItem(g_hwndList, &lvi);
goto ReFind;
}
lvi.iItem = iFound;
lvi.mask = LVIF_IMAGE | LVIF_TEXT;
lvi.pszText = si.sName;
lvi.iImage = !si.enabled;
ListView_SetItem(g_hwndList, &lvi);
}
sCorrection[0] = 0;
if (ss.progress)
{
switch (ss.progress)
{
case eProgressWaiting:
lstrcpy(sLatency, "Waiting...");
break;
case eProgressLookup:
lstrcpy(sLatency, "DNS Lookup...");
break;
case eProgressConnecting:
wsprintf(sLatency, "Connecting%d...", ss.iter + 1);
break;
case eProgressReading:
wsprintf(sLatency, "Reading%d...", ss.iter + 1);
break;
}
}
else
{
switch (si.status)
{
case eStatusNetError:
lstrcpy(sLatency, "Winsock Error");
break;
case eStatusNotResponding:
lstrcpy(sLatency, "No Response");
break;
case eStatusSlowResponding:
lstrcpy(sLatency, "Slow Response");
break;
case eStatusRefused:
lstrcpy(sLatency, "Refused");
break;
case eStatusNetDown:
lstrcpy(sLatency, "Network Error");
break;
case eStatusNoDNSEntry:
lstrcpy(sLatency, "No DNS entry");
break;
case eStatusDisabled:
lstrcpy(sLatency, "Disabled");
break;
case eStatusInterrupted:
lstrcpy(sLatency, "Interrupted");
break;
case eStatusUsed:
case eStatusThrownOut:
case eStatusTest:
wsprintf(sLatency, "%d", si.latency);
MillisecondsToText(si.correction, sCorrection);
if (si.status == eStatusThrownOut)
{
lstrcat(sCorrection, " (unused)");
}
if (si.status == eStatusTest)
{
lstrcat(sCorrection, " (test)");
}
break;
default:
sLatency[0] = 0;
}
}
ListView_SetItemText(g_hwndList, iFound, 1, sLatency);
ListView_SetItemText(g_hwndList, iFound, 2, sCorrection);
}
else if (iFound >= 0)
{
ListView_DeleteItem(g_hwndList, iFound);
}
}
}
void DisplayDirtyServers()
{
int i;
bool bAnyone = false;
for (i = 0; i < cMaxServers; i++)
{
ServerState& ss = g_ServerStates[i];
if (ss.dirty)
{
bAnyone = true;
DisplayServer(i, true);
}
}
if (g_nSortColumn && bAnyone)
{
ListView_SortItems(g_hwndList, ServerListCompareFunc, 0);
}
}
void SaveServer(int i)
{
char s[100];
wsprintf(s, "Server%03d", i);
if (g_ServerItems[i].sName[0])
{
SetReg(s, &g_ServerItems[i], sizeof(g_ServerItems[i]));
}
else
{
SetReg(s);
}
}
void DisplayAndSaveServer(int i)
{
DisplayServer(i);
SaveServer(i);
}
int AddServer(const char* s)
{
int i;
if (s && *s)
{
for (i = 0; i < cMaxServers; i++)
{
ServerItem& si = g_ServerItems[i];
if (!si.sName[0])
{
lstrcpyn(si.sName, s, sizeof(si.sName));
si.status = eStatusNothing;
si.enabled = TRUE;
g_ServerStates[i].dirty = true;
return i;
}
}
}
return -1;
}
void SetDefaultServers()
{
int i;
ZeroMemory(g_ServerItems, sizeof(g_ServerItems));
AddServer("bitsy.mit.edu");
AddServer("canon.inria.fr");
AddServer("chronos.univ-rennes1.fr");
AddServer("clock.llnl.gov");
AddServer("lerc-dns.lerc.nasa.gov");
AddServer("norad.arc.nasa.gov");
AddServer("ntp1.sony.com");
AddServer("ntps1-0.cs.tu-berlin.de");
AddServer("ntps1-1.cs.tu-berlin.de");
AddServer("ntps1-1.uni-erlangen.de");
AddServer("otc1.psu.edu");
AddServer("rackety.udel.edu");
AddServer("tick.usno.navy.mil");
AddServer("time-A.timefreq.bldrdoc.gov");
AddServer("time.nist.gov");
AddServer("time.service.uit.no");
AddServer("tock.usno.navy.mil");
AddServer("wwvb.erg.sri.com");
AddServer("wwvb.isi.edu");
AddServer("augean.eleceng.adelaide.edu.au");
AddServer("biofiz.mf.uni-lj.si");
AddServer("black-ice.cc.vt.edu");
AddServer("chime1.surfnet.nl");
AddServer("clock.psu.edu");
AddServer("clock1.unc.edu");
AddServer("clock-1.cs.cmu.edu");
AddServer("clock-2.cs.cmu.edu");
AddServer("delphi.cs.ucla.edu");
AddServer("esavax.esa.lanl.gov");
AddServer("fartein.ifi.uio.no");
AddServer("gazette.bcm.tmc.edu");
AddServer("gilbreth.ecn.purdue.edu");
AddServer("harbor.ecn.purdue.edu");
AddServer("jane.jpl.nasa.gov");
AddServer("kuhub.cc.ukans.edu");
AddServer("lane.cc.ukans.edu");
AddServer("louie.udel.edu");
AddServer("molecule.ecn.purdue.edu");
AddServer("noc.near.net");
AddServer("ns.unet.umn.edu");
AddServer("nss.unet.umn.edu");
AddServer("ntp.ctr.columbia.edu");
AddServer("ntp0.pipex.net");
AddServer("ntp0.strath.ac.uk");
AddServer("ntp1.pipex.net");
AddServer("ntp2.pipex.net");
AddServer("ntp2.sura.net");
AddServer("ntp-2.mcs.anl.gov");
AddServer("ntp0.cornell.edu");
AddServer("rolex.peachnet.edu");
AddServer("salmon.maths.tcd.ie");
AddServer("timelord.cs.uregina.ca");
AddServer("ticktock.wang.com");
AddServer("timex.cs.columbia.edu");
AddServer("timex.peachnet.edu");
AddServer("tmc.edu");
AddServer("tycho.usno.navy.mil");
AddServer("wuarchive.wustl.edu");
g_bFirstServerList = true;
for (i = 0; i < cMaxServers; i++)
{
DisplayAndSaveServer(i);
}
}
void LoadServerList()
{
int i;
char s[100];
if (g_bDefaultServers)
{
SetDefaultServers();
g_bDefaultServers = false;
}
else
{
for (i = 0; i < cMaxServers; i++)
{
wsprintf(s, "Server%03d", i);
if (!GetReg(s, &g_ServerItems[i]))
{
g_ServerItems[i].sName[0] = 0;
g_ServerItems[i].status = eStatusNothing;
g_ServerStates[i].dirty = true;
}
DisplayServer(i);
}
}
}
// ASYNCHRONOUS SOCKET/SERVER FUNCTIONS
// ---------------------------------------------------------------------------
void StartCorrect()
{
int i;
if (g_nRunningServerCount)
{
return;
}
g_bTesting = false;
g_LastAttemptTime = GetMillisecondsSince1900();
for (i = 0; i < cMaxServers; i++)
{
ServerItem& si = g_ServerItems[i];
ServerState& ss = g_ServerStates[i];
if (si.sName[0])
{
if (si.enabled)
{
ServerStart(i);
}
else
{
si.status = eStatusDisabled;
SaveServer(i);
ss.dirty = true;
}
}
}
DisplayDirtyServers();
}
void ServerReallyStart(int i)
{
ServerItem& si = g_ServerItems[i];
ServerState& ss = g_ServerStates[i];
// Wait no more than 20 seconds for user to connect
ss.timeoutTime = GetMillisecondsSince1900() + 20 * 1000;
g_nReallyRunningServerCount++;
ss.progress = eProgressLookup;
ss.ip = ntohl(inet_addr(si.sName));
if (ss.ip == INADDR_NONE)
{
ss.dirty = true;
ss.h = WSAAsyncGetHostByName(g_hDlg, WM_USER + 1,
si.sName, ss.hostentBuf, sizeof(ss.hostentBuf));
if (!ss.h)
{
ServerStop(i, eStatusNetError);
}
}
else
{
ServerGotIP(i);
}
}
void DealWithWaitingServers()
{
int i;
LONGLONG now = GetMillisecondsSince1900();
for (i = 0; i < cMaxServers; i++)
{
ServerState& ss = g_ServerStates[i];
if (ss.progress == eProgressWaiting && g_nReallyRunningServerCount < cMaxSockets)
{
ServerReallyStart(i);
}
if (now > g_timeoutTime &&
ss.progress > eProgressWaiting && now > ss.timeoutTime)
{
ServerStop(i, eStatusSlowResponding);
}
}
}
void ServerStart(int i)
{
ServerItem& si = g_ServerItems[i];
ServerState& ss = g_ServerStates[i];
// Global timeout of 120 seconds
g_timeoutTime = GetMillisecondsSince1900() + 120 * 1000;
ss.iter = 0;
si.status = eStatusNothing;
ss.progress = eProgressWaiting;
ss.dirty = true;
if (!g_nRunningServerCount++)
{
ChangeMessage(eMessageChecking);
SetDlgItemText(g_hDlg, IDOK, g_bTesting ? "Stop Testing...":
"Stop Correcting...");
}
DealWithWaitingServers();
}
void ServerDNS(HANDLE h, WORD error)
{
int i;
for (i = 0; i < cMaxServers; i++)
{
ServerState& ss = g_ServerStates[i];
if (ss.progress > eProgressWaiting && ss.h == h)
{
ServerItem& si = g_ServerItems[i];
ss.h = 0;
switch (error)
{
case 0:
ss.ip =
*(LPDWORD(((hostent*)ss.hostentBuf)->h_addr_list[0]));
ServerGotIP(i);
break;
case WSAHOST_NOT_FOUND:
case WSANO_DATA:
ServerStop(i, eStatusNoDNSEntry);
break;
default:
ServerStop(i, eStatusNetDown);
}
return;
}
}
}
void ServerGotIP(int i)
{
SOCKADDR_IN sinClient, sinServer;
int err;
ServerState& ss = g_ServerStates[i];
ServerItem& si = g_ServerItems[i];
sinServer.sin_family = AF_INET;
sinServer.sin_port = htons(37);
sinServer.sin_addr.S_un.S_addr = ss.ip;
sinClient.sin_family = AF_INET;
sinClient.sin_addr.S_un.S_addr = INADDR_ANY;
sinClient.sin_port = 0;
ss.s = socket(AF_INET, SOCK_STREAM, 0);
if (ss.s == INVALID_SOCKET)
{
ServerStop(i, eStatusNetError);
return;
}
err = bind(ss.s, (const struct sockaddr FAR *)&sinClient,
sizeof(SOCKADDR_IN));
if (err)
{
ServerStop(i, eStatusNetError);
return;
}
err = 1;
err = setsockopt(ss.s, SOL_SOCKET, SO_DONTLINGER, (LPCSTR)&err, sizeof(err));
if (err)
{
ServerStop(i, eStatusNetError);
return;
}
err = WSAAsyncSelect(ss.s, g_hDlg, WM_USER + 2, FD_CONNECT | FD_READ);
if (err)
{
ServerStop(i, eStatusNetError);
return;
}
ss.progress = eProgressConnecting;
ss.dirty = true;
err = connect(ss.s, (const struct sockaddr FAR *)&sinServer,
sizeof(SOCKADDR_IN));
if (err && WSAGetLastError() != WSAEWOULDBLOCK)
{
ServerStop(i, eStatusNetError);
return;
}
// Wait no more than 20 seconds for connect operation
ss.timeoutTime = GetMillisecondsSince1900() + 20 * 1000;
}
void ServerConnected(int i, int err)
{
ServerState& ss = g_ServerStates[i];
ServerItem& si = g_ServerItems[i];
if (err)
{
ServerStop(i, (err == WSAECONNREFUSED) ?
eStatusRefused : eStatusNotResponding);
}
else
{
ss.latency[ss.iter] = GetMilliseconds();
ss.progress = eProgressReading;
ss.dirty = true;
}
// Cancel global timeout
g_timeoutTime = 0;
// Wait no more than 20 seconds for a read operation
ss.timeoutTime = GetMillisecondsSince1900() + 20 * 1000;
}
void ServerRead(int i, int err)
{
DWORD temp;
LONGLONG correction;
ServerState& ss = g_ServerStates[i];
ServerItem& si = g_ServerItems[i];
if (err)
{
ServerStop(i, eStatusNotResponding);
}
else
{
err = recv(ss.s, (char FAR *)&temp, 4, 0);
if (err != 4)
{
ServerStop(i, eStatusNotResponding);
return;
}
correction = ntohl(temp) * LONGLONG(1000);
correction -= GetMillisecondsSince1900();
ss.latency[ss.iter] = GetMilliseconds() - ss.latency[ss.iter];
ss.correction[ss.iter] =
correction +
(ss.latency[ss.iter] >> 1) +
g_LocalTimeOffset;
ss.dirty = true;
if (++ss.iter < cMaxIter)
{
WSAAsyncSelect(ss.s, g_hDlg, 0, 0);
closesocket(ss.s);
ss.s = 0;
ServerGotIP(i);
}
else
{
ServerTimeAdjust(i);
}
}
}
void ServerTimeAdjust(int i)
{
LONGLONG correction;
DWORD latency;
int m, n;
ServerState& ss = g_ServerStates[i];
ServerItem& si = g_ServerItems[i];
for (m = 0; m < cMaxIter - 1; m++)
{
for (n = m + 1; n < cMaxIter; n++)
{
if (ss.latency[n] < ss.latency[m])
{
latency = ss.latency[n];
correction = ss.correction[n];
ss.latency[n] = ss.latency[m];
ss.correction[n] = ss.correction[m];
ss.latency[m] = latency;
ss.correction[m] = correction;
}
}
}
correction = 0;
for (m = 0; m < cUsedIter; m++)
{
correction += ss.correction[m];
}
si.correction = correction / cUsedIter;
latency = 0;
for (m = 0; m < cMaxIter; m++)
{
latency += ss.latency[m];
}
si.latency = latency / cMaxIter;
ServerStop(i, g_bTesting ? eStatusTest:eStatusUsed);
}
void ServerStop(int i, int status)
{
ServerState& ss = g_ServerStates[i];
if (ss.progress)
{
if (ss.progress > eProgressWaiting)
{
--g_nReallyRunningServerCount;
if (ss.h)
{
WSACancelAsyncRequest(ss.h);
ss.h = 0;
}
if (ss.s)
{
WSAAsyncSelect(ss.s, g_hDlg, 0, 0);
closesocket(ss.s);
ss.s = 0;
}
}
ss.progress = eProgressNot;
g_ServerItems[i].status = status;
SaveServer(i);
ss.dirty = true;
if (!(--g_nRunningServerCount))
{
bool bTimeCorrected = false;
if (!g_bTesting)
{
bTimeCorrected = DoSystemTimeCorrection();
}
ChangeMessage(eMessageIdle);
SetDlgItemText(g_hDlg, IDOK, "Correct NOW!");
DisplayDirtyServers();
if (bTimeCorrected && g_bFirstServerList)
{
g_bAutoQuit = false;
g_bFirstServerList = false;
if (MessageBox(g_hDlg, "Now that you've successfully "
"corrected your clock using several time servers, "
"AtomicTime will remove all but the four fastest "
"so that your next correction will run faster "
"and be more reliable.\n\n"
"(Alternately, you can right click on the server "
"list and edit them yourself.)", "AtomicTime",
MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK)
{
Keep4BestServers();
}
}
}
}
}
bool DoSystemTimeCorrection()
{
LONGLONG correction, correctionTime, worstCorrection, llabs;
int i, iWorstCorrection, count;
ThrewAwayWorstServer:
correction = 0;
count = 0;
for (i = 0; i < cMaxServers; i++)
{
ServerItem& si = g_ServerItems[i];
if (si.sName[0] && si.status == eStatusUsed)
{
correction += si.correction;
count++;
}
}
if (!count)
{
g_bProblem = true;
return false;
}
correction /= count;
worstCorrection = 0;
for (i = 0; i < cMaxServers; i++)
{
ServerItem& si = g_ServerItems[i];
if (si.sName[0] && si.status == eStatusUsed)
{
llabs = si.correction - correction;
if (llabs < 0)
{
llabs = -llabs;
}
if (llabs >= worstCorrection)
{
worstCorrection = llabs;
iWorstCorrection = i;
}
}
}
if (worstCorrection >= 2000)
{
g_ServerItems[iWorstCorrection].status = eStatusThrownOut;
g_ServerStates[iWorstCorrection].dirty = true;
SaveServer(iWorstCorrection);
goto ThrewAwayWorstServer;
}
correctionTime = OffsetMillisecondsSince1900(correction);
g_LastCorrectionValue = correction;
g_LastCorrectionTime = correctionTime;
g_LastAttemptTime = g_LastCorrectionTime;
SetReg("LastCorrectionTime", &g_LastCorrectionTime,
sizeof(g_LastCorrectionTime));
SetReg("LastCorrectionValue", &g_LastCorrectionValue,
sizeof(g_LastCorrectionValue));
if (g_bAutoQuit)
{
SetTimer(g_hDlg, 1, 10000, NULL);
}
SendMessage(HWND_TOPMOST, WM_TIMECHANGE, 0, 0);
#ifdef _DEBUG
{
char s1[100], s2[100];
MillisecondsToText(g_LastCorrectionValue, s1);
FormatMillisecondsSince1900(g_LastCorrectionTime, s2);
dprintf(("Corrected %s at %s\r\n", s1, s2));
}
#endif
return true;
}
void Keep4BestServers()
{
int i, count;
count = 0;
for (i = 0; i < cMaxServers; i++)
{
ServerItem& si = g_ServerItems[i];
if (si.sName[0])
{
if (si.status != eStatusUsed && si.status != eStatusTest)
{
si.sName[0] = 0;
DisplayAndSaveServer(i);
}
else
{
count++;
}
}
}
while (count > 4)
{
DWORD worstLatency = 0;
int iWorst;
for (i = 0; i < cMaxServers; i++)
{
ServerItem& si = g_ServerItems[i];
if (si.sName[0] && si.latency >= worstLatency)
{
worstLatency = si.latency;
iWorst = i;
}
}
ServerStop(iWorst, eStatusInterrupted);
g_ServerItems[iWorst].sName[0] = 0;
DisplayAndSaveServer(iWorst);
count--;
}
ListView_SortItems(g_hwndList, ServerListCompareFunc, 0);
}
void AbortAllServers(int status)
{
int i;
for (i = 0; i < cMaxServers; i++)
{
ServerStop(i, status);
}
}
// USER INTERFACE STUFF
// ---------------------------------------------------------------------------
void Init()
{
NOTIFYICONDATA nid;
char s[100];
HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ATOMICTIME2));
LV_COLUMN lvc;
RECT rect;
HIMAGELIST himl;
GetReg("LastCorrectionTime", &g_LastCorrectionTime);
GetReg("LastCorrectionValue", &g_LastCorrectionValue);
g_LastAttemptTime = g_LastCorrectionTime;
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hDlg;
nid.uID = 0;
nid.uFlags = NIF_MESSAGE;
nid.uCallbackMessage = WM_USER;
Shell_NotifyIcon(NIM_ADD, &nid);
ChangeMessage(eMessageIdle);
SetClassLong(g_hDlg, GCL_HICON, (LONG)hIcon);
// Setup Server List View
himl = ImageList_Create(11, 11, ILC_MASK, 1, 1);
ImageList_AddIcon(himl, hIcon);
ListView_SetImageList(g_hwndList, himl, LVSIL_SMALL);
ListView_SetBkColor(g_hwndList, GetSysColor(COLOR_WINDOW));
GetClientRect(g_hwndList, &rect);
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvc.fmt = LVCFMT_LEFT;
lvc.cx = rect.right * 50 / 100;
lvc.pszText = "Time Server";
ListView_InsertColumn(g_hwndList, 1, &lvc);
lvc.cx = rect.right * 25 / 100;
lvc.pszText = "Latency (ms)";
ListView_InsertColumn(g_hwndList, 2, &lvc);
lvc.cx = rect.right * 25 / 100;
lvc.pszText = "Correction";
ListView_InsertColumn(g_hwndList, 3, &lvc);
ZeroMemory(g_ServerStates, sizeof(g_ServerStates));
LoadServerList();
ListView_SortItems(g_hwndList, ServerListCompareFunc, 0);
// Set up Trackbar
SendDlgItemMessage(g_hDlg, IDC_SLIDER_SCHEDULE, TBM_SETRANGE, TRUE,
MAKELONG(0, 9));
GetReg("Schedule", &g_nSchedule);
SendDlgItemMessage(g_hDlg, IDC_SLIDER_SCHEDULE, TBM_SETPOS, TRUE,
g_nSchedule);
SetTrackCaption();
// Set the checkbox
SendDlgItemMessage(g_hDlg, IDC_CORRECT_ON_LAUNCH, BM_SETCHECK,
g_bCorrectOnLaunch, 0);
// Set up offset
GetReg("Offset", &g_LocalTimeOffset);
MillisecondsToText(g_LocalTimeOffset, s);
SetDlgItemText(g_hDlg, IDC_EDIT_OFFSET, s);
DeleteObject(hIcon);
if (!g_nRunningServerCount &&
( g_bCorrectOnLaunch || (g_mode && g_nSchedule == 1)))
{
g_bAutoQuit = (g_nSchedule <= 1) ? true:false;
StartCorrect();
}
}
void Cleanup()
{
NOTIFYICONDATA nid;
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hDlg;
nid.uID = 0;
Shell_NotifyIcon(NIM_DELETE, &nid);
KillTimer(g_hDlg, 0);
if (g_nRunningServerCount)
{
AbortAllServers(eStatusInterrupted);
}
KillTimer(g_hDlg, 1);
KillTimer(g_hDlg, 2);
}
void ChangeMessage(int nMessageIndex)
{
char s[200];
NOTIFYICONDATA nid;
KillTimer(g_hDlg, 2);
switch (nMessageIndex)
{
case eMessageChecking:
g_bProblem = false;
SetTimer(g_hDlg, 2, 200, NULL);
lstrcpy(s, "Checking time servers...");
break;
case eMessageTesting:
g_bProblem = false;
SetTimer(g_hDlg, 2, 200, NULL);
lstrcpy(s, "Testing time servers...");
break;
default:
*s = 0;
if (g_LastCorrectionTime)
{
char s1[100], s2[100];
MillisecondsToText(g_LastCorrectionValue, s1);
FormatMillisecondsSince1900(g_LastCorrectionTime, s2);
wsprintf(s, "Corrected %s at %s", s1, s2);
}
}
SetDlgItemText(g_hDlg, IDC_TEXT_STATUS, s);
if (g_bProblem)
{
lstrcpy(s, "Unsuccessful correction! Double-click for details...");
}
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(
g_bProblem ? IDI_ATOMICTIME3:IDI_ATOMICTIME1));
nid.hWnd = g_hDlg;
nid.uID = 0;
nid.uFlags = NIF_TIP | NIF_ICON;
lstrcpy(nid.szTip, s);
Shell_NotifyIcon(NIM_MODIFY, &nid);
DeleteObject(nid.hIcon);
}
void SetTrackCaption()
{
char* s;
switch (g_nSchedule)
{
case 1:
s = "On Startup";
g_CorrectionInterval = 0;
break;
case 2:
#ifdef _DEBUG
s = "Every 20 seconds";
g_CorrectionInterval = LONGLONG(1000) * 20;
#else
s = "Every 5 Minutes";
g_CorrectionInterval = LONGLONG(1000) * 60 * 5;
#endif
break;
case 3:
s = "Every 15 Minutes";
g_CorrectionInterval = LONGLONG(1000) * 60 * 15;
break;
case 4:
s = "Hourly";
g_CorrectionInterval = LONGLONG(1000) * 3600;
break;
case 5:
s = "Every Six Hours";
g_CorrectionInterval = LONGLONG(1000) * 3600 * 6;
break;
case 6:
s = "Daily";
g_CorrectionInterval = LONGLONG(1000) * 3600 * 24;
break;
case 7:
s = "Every 3 Days";
g_CorrectionInterval = LONGLONG(1000) * 3600 * 24 * 3;
break;
case 8:
s = "Weekly";
g_CorrectionInterval = LONGLONG(1000) * 3600 * 24 * 7;
break;
case 9:
s = "Monthly";
g_CorrectionInterval = LONGLONG(1000) * 3600 * 24 * 30;
break;
default:
g_CorrectionInterval = 0;
s = "No Scheduled Interval"; break;
}
SetRunOnStart(g_nSchedule != 0);
SetDlgItemText(g_hDlg, IDC_TEXT_SCHEDULE, s);
}
void RemoveSelectedServers()
{
int i = ListView_GetSelectedCount(g_hwndList);
if (i)
{
char s[200];
wsprintf(s, "Are you sure you want to delete %d server(s)?", i);
if (MessageBox(g_hDlg, s, "Warning!", MB_OKCANCEL | MB_ICONEXCLAMATION)
== IDOK)
{
while ((i = ListView_GetNextItem(g_hwndList, -1,
LVNI_ALL | LVNI_SELECTED)) >= 0)
{
LV_ITEM lvi;
lvi.mask = LVIF_PARAM;
lvi.iItem = i;
ListView_GetItem(g_hwndList, &lvi);
ServerStop(lvi.lParam, eStatusInterrupted);
g_ServerItems[lvi.lParam].sName[0] = 0;
DisplayAndSaveServer(lvi.lParam);
}
}
ListView_SortItems(g_hwndList, ServerListCompareFunc, 0);
}
}
void ToggleSelectedServers()
{
int i = -1;
while ((i = ListView_GetNextItem(g_hwndList, i,
LVNI_ALL | LVNI_SELECTED)) >= 0)
{
LV_ITEM lvi;
lvi.mask = LVIF_PARAM;
lvi.iItem = i;
ListView_GetItem(g_hwndList, &lvi);
ServerStop(lvi.lParam, eStatusInterrupted);
g_ServerItems[lvi.lParam].enabled ^= 1;
DisplayAndSaveServer(lvi.lParam);
}
ListView_SortItems(g_hwndList, ServerListCompareFunc, 0);
}
void TestSelectedServers()
{
int i = ListView_GetSelectedCount(g_hwndList);
if (i)
{
if (g_nRunningServerCount)
{
return;
}
g_bTesting = true;
i = -1;
while ((i = ListView_GetNextItem(g_hwndList, i,
LVNI_ALL | LVNI_SELECTED)) >= 0)
{
LV_ITEM lvi;
lvi.mask = LVIF_PARAM;
lvi.iItem = i;
ListView_GetItem(g_hwndList, &lvi);
ServerStart(lvi.lParam);
}
}
}
void ServerListPopup(BOOL useMousePos)
{
POINT pt;
HMENU hMenu = LoadMenu(g_hInstance,
MAKEINTRESOURCE(IDM_SERVER_LIST)),
hSubMenu = GetSubMenu(hMenu, 0);
int i, count, total;
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STATE;
mii.fState = MFS_GRAYED;
if (useMousePos)
{
GetCursorPos(&pt);
}
else
{
pt.x = pt.y = 0;
ClientToScreen(g_hwndList, &pt);
}
count = total = 0;
for (i = 0; i < cMaxServers; i++)
{
ServerItem& si = g_ServerItems[i];
if (si.sName[0])
{
total++;
if (si.status != eStatusNothing)
{
count++;
}
}
}
if (count <= 4)
{
SetMenuItemInfo(hSubMenu, ID_KEEP4BEST, FALSE, &mii);
}
if (total == cMaxServers)
{
SetMenuItemInfo(hSubMenu, ID_ADD_SERVER, FALSE, &mii);
}
count = ListView_GetSelectedCount(g_hwndList);
if (!count)
{
SetMenuItemInfo(hSubMenu, ID_DELETE_SERVER, FALSE, &mii);
SetMenuItemInfo(hSubMenu, ID_TOGGLE_SERVER, FALSE, &mii);
SetMenuItemInfo(hSubMenu, ID_CHANGE_SERVER, FALSE, &mii);
SetMenuItemInfo(hSubMenu, ID_TEST_SERVER, FALSE, &mii);
}
TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, g_hDlg, NULL);
DestroyMenu(hMenu);
}
// Need this for the sorting of the list view
//
// Column 0 - Name sorting
// Column 1 - Latency sorting
// If correction in progress, sort on iteration
// Then, sort on state
// If states are equal, sort on name (forward alphabetical)
// Finally, sort on latencies of servers than completed OK
// Column 2 - Correction sorting
// Just like latency, but uses correction instead
//
int CALLBACK ServerListCompareFunc(LPARAM lParam1, LPARAM lParam2,
LPARAM)
{
ServerItem& si1 = g_ServerItems[lParam1];
ServerState& ss1 = g_ServerStates[lParam1];
ServerItem& si2 = g_ServerItems[lParam2];
ServerState& ss2 = g_ServerStates[lParam2];
int status1 = si1.status, status2 = si2.status, result = 0;
if (status1 >= eStatusThrownOut)
{
status1 = eStatusUsed;
}
if (ss1.progress && !status1)
{
status1 = ss1.iter - cMaxIter;
}
if (status2 >= eStatusThrownOut)
{
status2 = eStatusUsed;
}
if (ss2.progress && !status2)
{
status2 = ss2.iter - cMaxIter;
}
if (g_nSortColumn)
{
if (si1.enabled != si2.enabled)
{
result = si2.enabled - si1.enabled;
}
else if (status1 < status2)
{
result = (g_nSortColumn == 1) ? g_nSortPolarity:1;
}
else if (status1 > status2)
{
result = (g_nSortColumn == 1) ? -g_nSortPolarity:-1;
}
else if (status1 == eStatusUsed)
{
if (g_nSortColumn == 1)
{
if (si1.latency < si2.latency)
{
result = -g_nSortPolarity;
}
if (si1.latency > si2.latency)
{
result = g_nSortPolarity;
}
}
else
{
if (status2 == eStatusUsed)
{
if (si1.correction < si2.correction)
{
result = -g_nSortPolarity;
}
if (si1.correction > si2.correction)
{
result = g_nSortPolarity;
}
}
}
}
else
{
if (ss1.progress + ss1.iter * 10 < ss2.progress + ss2.iter * 10)
{
result = -g_nSortPolarity;
}
}
}
if (!result)
{
result = lstrcmp(si1.sName, si2.sName);
if (!g_nSortColumn)
{
result *= g_nSortPolarity;
}
}
return result;
}
BOOL CALLBACK MyDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static BOOL theTracking = FALSE;
WINDOWPLACEMENT wp;
int i;
switch (uMsg)
{
case WM_INITDIALOG:
{
WINDOWPLACEMENT wp2;
RECT& r = wp.rcNormalPosition, &r2 = wp2.rcNormalPosition;
g_hDlg = hDlg;
g_hwndList = GetDlgItem(hDlg, IDC_LIST_SERVERS);
SetWindowText(hDlg, cWindowName);
Init();
if (!GetReg("WindowPlacement", &wp) ||
r.left > GetSystemMetrics(SM_CXFULLSCREEN) - 20 ||
r.right < 20 ||
r.top > GetSystemMetrics(SM_CYFULLSCREEN) - 20 ||
r.bottom < 20 ||
wp.length != sizeof(wp))
{
wp.length = sizeof(wp);
GetWindowPlacement(hDlg, &wp);
OffsetRect(&r,
(GetSystemMetrics(SM_CXFULLSCREEN) -
(r.right - r.left)) >> 1,
(GetSystemMetrics(SM_CYFULLSCREEN) -
(r.bottom - r.top)) >> 1);
}
else
{
wp2.length = sizeof(wp2);
GetWindowPlacement(hDlg, &wp2);
r.right = r.left + r2.right - r2.left;
r.bottom = r.top + r2.bottom - r2.top;
}
wp.showCmd = (g_mode || g_bCorrectOnLaunch) ? SW_HIDE:SW_SHOW;
SetTimer(g_hDlg, 0, 10000, NULL);
SetWindowPlacement(hDlg, &wp);
return TRUE;
}
case WM_SHOWWINDOW:
KillTimer(g_hDlg, 0);
SetTimer(g_hDlg, 0, wParam ? 100:10000, NULL);
return TRUE;
case WM_CLOSE:
goto Close;
case WM_MOVE:
wp.length = sizeof(wp);
GetWindowPlacement(hDlg, &wp);
SetReg("WindowPlacement", &wp, sizeof(wp));
return TRUE;
case WM_TIMER:
if (wParam == 0)
{
char s1[100], s2[100];
LONGLONG now = GetMillisecondsSince1900();
FormatMillisecondsSince1900(now, s1);
GetDlgItemText(g_hDlg, IDC_TEXT_TIME, s2, sizeof(s2));
if (lstrcmp(s1, s2))
{
SetDlgItemText(g_hDlg, IDC_TEXT_TIME, s1);
}
if (g_CorrectionInterval)
{
if (now >= g_CorrectionInterval + g_LastAttemptTime &&
!g_nRunningServerCount)
{
StartCorrect();
}
}
}
else if (wParam == 1)
{
KillTimer(g_hDlg, 1);
if (g_bAutoQuit)
{
DestroyWindow(hDlg);
}
}
else if (wParam == 2)
{
NOTIFYICONDATA nid;
static flipflop;
flipflop = !flipflop;
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hIcon = LoadIcon(g_hInstance,
MAKEINTRESOURCE(flipflop ? IDI_ATOMICTIME1:IDI_ATOMICTIME2));
nid.hWnd = g_hDlg;
nid.uID = 0;
nid.uFlags = NIF_ICON;
Shell_NotifyIcon(NIM_MODIFY, &nid);
DeleteObject(nid.hIcon);
DealWithWaitingServers();
DisplayDirtyServers();
}
return TRUE;
case WM_ENDSESSION:
DestroyWindow(hDlg);
return TRUE;
case WM_HSCROLL:
g_nSchedule = SendDlgItemMessage(g_hDlg, IDC_SLIDER_SCHEDULE,
TBM_GETPOS, 0, 0);
SetReg("Schedule", &g_nSchedule, sizeof(g_nSchedule));
SetTrackCaption();
return TRUE;
case WM_USER: // Tray icon message
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
{
if (!theTracking)
{
POINT pt;
HMENU hMenu = LoadMenu(g_hInstance,
MAKEINTRESOURCE(IDM_ATOMICTIME));
MENUITEMINFO mii;
char s[100];
g_bAutoQuit = false;
GetCursorPos(&pt);
SetForegroundWindow(hDlg);
theTracking = TRUE;
SetMenuDefaultItem(GetSubMenu(hMenu, 0), 0, TRUE);
s[0] = '&';
GetDlgItemText(g_hDlg, IDOK, s + 1, sizeof(s) - 1);
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE;
mii.fType = MFT_STRING;
mii.dwTypeData = s;
SetMenuItemInfo(GetSubMenu(hMenu, 0), IDOK, FALSE, &mii);
TrackPopupMenu(GetSubMenu(hMenu, 0),
TPM_LEFTALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, g_hDlg, NULL);
DestroyMenu(hMenu);
theTracking = FALSE;
}
}
else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_RBUTTONDBLCLK)
{
SendMessage(hDlg, WM_COMMAND, ID_OPTIONS, 0);
}
break;
case WM_USER + 1: // Asynch Winsock callback for DNS
ServerDNS((HANDLE)wParam, WSAGETASYNCERROR(lParam));
return TRUE;
case WM_USER + 2: // Asynch Winsock callback for connect/read
{
for (i = 0; i < cMaxServers; i++)
{
ServerState& ss = g_ServerStates[i];
if (ss.progress > eProgressWaiting && ss.s == (SOCKET)wParam)
{
switch (WSAGETSELECTEVENT(lParam))
{
case FD_CONNECT:
ServerConnected(i, WSAGETSELECTERROR(lParam));
break;
case FD_READ:
ServerRead(i, WSAGETSELECTERROR(lParam));
break;
}
}
}
}
return TRUE;
case WM_NOTIFY: // messages from server list view
if (wParam == IDC_LIST_SERVERS)
{
NM_LISTVIEW* pnmv = (NM_LISTVIEW*)lParam;
switch (pnmv->hdr.code)
{
case LVN_ENDLABELEDIT: // someone entered a server name
{
LV_ITEM* plvi = &((LV_DISPINFO *)lParam)->item;
ServerName sOldName;
LV_ITEM lvi;
if (!(plvi->mask & LVIF_TEXT))
{
return TRUE; // Not a text edit?
}
lvi.mask = LVIF_PARAM | LVIF_TEXT;
lvi.iItem = plvi->iItem;
lvi.cchTextMax = sizeof(sOldName);
lvi.pszText = sOldName;
ListView_GetItem(g_hwndList, &lvi);
// If this is NULL, they pressed escape
// instead of changing the name
if (plvi->pszText)
{
ServerStop(lvi.lParam, eStatusInterrupted);
lstrcpyn(g_ServerItems[lvi.lParam].sName,
plvi->pszText,
sizeof(ServerName));
DisplayAndSaveServer(lvi.lParam);
ListView_SortItems(g_hwndList,
ServerListCompareFunc, 0);
}
}
return TRUE;
case LVN_KEYDOWN:
switch (((LV_KEYDOWN *)lParam)->wVKey)
{
case VK_APPS:
case VK_RETURN:
ServerListPopup(FALSE);
return TRUE;
case VK_DELETE:
RemoveSelectedServers();
return TRUE;
}
break;
case LVN_COLUMNCLICK:
{
i = ((NM_LISTVIEW *)lParam)->iSubItem;
if (i != g_nSortColumn)
{
g_nSortColumn = i;
g_nSortPolarity = 1;
}
else
{
g_nSortPolarity *= -1;
}
ListView_SortItems(g_hwndList,
ServerListCompareFunc, 0);
}
return TRUE;
case NM_RCLICK:
ServerListPopup(TRUE);
return TRUE;
}
}
return FALSE;
case WM_COMMAND:
// We cannot be getting any real input during an edit
// session of the server list. However, the ID of the
// edit box is often the same as IDOK. This check will
// prevent any confusion (although it is ugly)
if (g_hwndList && ListView_GetEditControl(g_hwndList))
{
return FALSE;
}
switch(LOWORD(wParam))
{
case IDOK:
if (g_nRunningServerCount)
{
AbortAllServers(eStatusInterrupted);
return TRUE;
}
// Fall through to IDYES
case IDYES:
if (!g_nRunningServerCount)
{
StartCorrect();
}
return TRUE;
case IDCANCEL:
Close:
ShowWindow(hDlg, SW_HIDE);
if (g_CorrectionInterval)
{
// If there is a timed-correction waiting to happen,
// do not shut the app down, leave the tray icon alive
return TRUE;
}
// Otherwise, fall through and shut down
case ID_QUIT: // Tray icon quit message
DestroyWindow(hDlg);
return TRUE;
case ID_OPTIONS: // Tray icon wants the big window
g_bProblem = false;
if (!g_nRunningServerCount)
{
ChangeMessage(eMessageIdle);
}
ShowWindow(hDlg, SW_NORMAL);
return TRUE;
case IDC_CORRECT_ON_LAUNCH: // Checkbox
g_bCorrectOnLaunch = SendDlgItemMessage(hDlg,
IDC_CORRECT_ON_LAUNCH, BM_GETCHECK, 0, 0) ? true:false;
if (g_bCorrectOnLaunch)
{
if (MessageBox(g_hDlg,
"This will prevent the options window from appearing "
"the next time you run AtomicTime, and instead, do a "
"silent time correction & quit. It is intended for "
"people who want a command line utility. You "
"may combine this with scheduled corrections as well.\n\n"
"One you enable this, the only way to get back to "
"the options window is to click on the AtomicTime "
"icon in the system tray during the ten seconds it "
"appears there.\n\n"
"Do you still want to use this option?",
"Note!", MB_YESNO | MB_ICONINFORMATION) == IDNO)
{
g_bCorrectOnLaunch = false;
SendDlgItemMessage(hDlg,
IDC_CORRECT_ON_LAUNCH, BM_SETCHECK, 0, 0);
return TRUE;
}
}
SetReg("CorrectOnLaunch", &g_bCorrectOnLaunch,
sizeof(g_bCorrectOnLaunch));
return TRUE;
case ID_DEFAULT_SERVERS:
if (MessageBox(g_hDlg,
"Are you sure you want to reset the server list?",
"Warning!", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK)
{
AbortAllServers(eStatusInterrupted);
SetDefaultServers();
ListView_SortItems(g_hwndList, ServerListCompareFunc, 0);
}
return TRUE;
case ID_DELETE_SERVER:
RemoveSelectedServers();
return TRUE;
case ID_TEST_SERVER:
TestSelectedServers();
return TRUE;
case ID_TOGGLE_SERVER:
ToggleSelectedServers();
return TRUE;
case ID_KEEP4BEST:
Keep4BestServers();
return TRUE;
case ID_CHANGE_SERVER:
{
i = ListView_GetNextItem(g_hwndList, -1,
LVNI_ALL | LVNI_FOCUSED);
if (i >= 0)
{
ListView_EditLabel(g_hwndList, i);
}
}
return TRUE;
case ID_ADD_SERVER:
{
i = AddServer("New Server");
if (i >= 0)
{
LV_FINDINFO lfi;
int iFound;
DisplayAndSaveServer(i);
lfi.flags = LVFI_PARAM;
lfi.lParam = i;
iFound = ListView_FindItem(g_hwndList, -1, &lfi);
if (iFound >= 0)
{
ListView_EditLabel(g_hwndList, iFound);
}
}
}
return TRUE;
case IDC_EDIT_OFFSET:
if (HIWORD(wParam) == EN_KILLFOCUS)
{
char s[100];
GetDlgItemText(hDlg, IDC_EDIT_OFFSET, s, sizeof(s));
g_LocalTimeOffset = TextToMilliseconds(s);
SetReg("Offset", &g_LocalTimeOffset,
sizeof(g_LocalTimeOffset));
MillisecondsToText(g_LocalTimeOffset, s);
SetDlgItemText(hDlg, IDC_EDIT_OFFSET, s);
}
return TRUE;
}
break;
case WM_SYSCOLORCHANGE:
ListView_SetBkColor(g_hwndList, GetSysColor(COLOR_WINDOW));
break;
case WM_DESTROY:
Cleanup();
g_hDlg = 0;
PostQuitMessage(0);
return TRUE;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int)
{
DWORD version;
MSG msg;
WSADATA WSAData;
HWND hWnd;
if (GetReg("Version", &version))
{
if (version != cVersion)
{
if (MessageBox(0, "Registry settings of other versions of AtomicTime "
"will be erased if you run this version.", cWindowName,
MB_OKCANCEL | MB_ICONSTOP) == IDCANCEL)
{
return 0;
}
if (RegDeleteKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\AtomicTime"))
{
Fatal("Cannot erase old Registry key");
}
}
else
{
g_bDefaultServers = false;
}
}
GetReg("CorrectOnLaunch", &g_bCorrectOnLaunch);
switch(lpCmdLine[0])
{
case 's': case 'u':
g_mode = lpCmdLine[0];
break;
}
if ((hWnd = FindWindow(NULL, cWindowName)) != NULL)
{
if (g_bCorrectOnLaunch)
{
SendMessage(hWnd, WM_COMMAND, IDYES, 0);
return 0;
}
else if (g_mode == 'u')
{
SendMessage(hWnd, WM_COMMAND, ID_QUIT, 0);
}
else
{
ShowWindow(hWnd, SW_SHOW);
SetForegroundWindow(hWnd);
return 0;
}
}
if (g_mode == 'u')
{
SetRunOnStart(false);
RegDeleteKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\AtomicTime");
RegDeleteKey(HKEY_LOCAL_MACHINE, cUninstallString);
Fatal("Uninstall complete. Now delete AtomicTime files.");
return 0;
}
if (!OffsetMillisecondsSince1900(0))
{
if (g_mode == 's')
{
return 0;
}
Fatal("You do not have privileges to set the system time."
"\n\nLog on as an Administrator instead.");
}
// Initialize Windows sockets version 1.01
if (WSAStartup(0x101, &WSAData))
{
Fatal("Could not open windows sockets.");
}
g_bWinsock = true;
g_hInstance = hInstance;
InitCommonControls(); // Need this for the list view
version = cVersion;
SetReg("Version", &version, sizeof(version));
if (!CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ATOMICTIME),
NULL, (DLGPROC)MyDialogProc))
{
Fatal("Cannot open window");
}
while (GetMessage(&msg, NULL, NULL, NULL))
{
if (!IsWindow(g_hDlg) || !IsDialogMessage(g_hDlg, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if (g_bWinsock)
{
WSACleanup();
}
return msg.wParam;
}
|