X
xieliwei
Hello, I'm attempting to write a WinNT service that basically changes
the time zone and forces a resync with a time server on initialisation.
The service will then restore the original time zone upon stopping and
force another resync. I have two versions that does the same thing, the
older one works perfectly but I did not like its structure. Thus I
followed another template, which formed the new version. However, the
new version works, but on shutdown, it finishes changing the time zone
and resyncing and then crashes. If I were to remove the time zone and
resyncing commands, the service terminates successfully.
The following is the old code that works fine:
<noscript><code>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define SLEEP_TIME 1800000
#define RESET_ON_MANUAL_STOP 1
#define LOCAL_TIME_ZONE "Singapore Standard Time"
#define FOREIGN_TIME_ZONE "GMT Standard Time"
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();
void ChangeTimeZone(char zone[])
{
char command[150];
command[0]='\0';
strcat(command,"RunDLL32 shell32.dll,Control_RunDLL
%SystemRoot%\\system32\\TIMEDATE.cpl,,/Z ");
strcat(command,zone);
system(command);
return;
}
void UpdateTime()
{
system("w32tm /resync /nowait");
return;
}
void main()
{
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = "TimeHandler";
ServiceTable[0].lpServiceProc =
(LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
// Start the control dispatcher thread for our service
StartServiceCtrlDispatcher(ServiceTable);
}
void ServiceMain(int argc, char** argv)
{
int error;
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
hStatus = RegisterServiceCtrlHandler(
"TimeHandler",
(LPHANDLER_FUNCTION)ControlHandler);
if (hStatus == (SERVICE_STATUS_HANDLE)0)
{
// Registering Control Handler failed
return;
}
// Initialize Service
error = InitService();
if (error)
{
// Initialization failed
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
// We report the running status to SCM.
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);
// The worker loop of a service
while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
{
Sleep(SLEEP_TIME);
UpdateTime();
return;
}
// Service initialization
int InitService()
{
ChangeTimeZone(LOCAL_TIME_ZONE);
UpdateTime();
return 0;
}
// Control handler function
void ControlHandler(DWORD request)
{
switch(request)
{
case SERVICE_CONTROL_STOP:
if(RESET_ON_MANUAL_STOP)
{
ChangeTimeZone(FOREIGN_TIME_ZONE);
UpdateTime();
}
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
case SERVICE_CONTROL_SHUTDOWN:
ChangeTimeZone(FOREIGN_TIME_ZONE);
UpdateTime();
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
default:
break;
}
// Report current status
SetServiceStatus (hStatus, &ServiceStatus);
return;
}
</code></noscript>
The following is the newer code based on another template, which
crashes on termination:
<noscript><code>
#include <stdio.h>
#include <windows.h>
#define SLEEP_TIME 1800000
#define RESET_ON_MANUAL_STOP 1
#define LOCAL_TIME_ZONE "Singapore Standard Time"
#define FOREIGN_TIME_ZONE "GMT Standard Time"
SERVICE_STATUS_HANDLE serviceStatusHandle;
void ServiceMain(DWORD argc, LPSTR *argv);
BOOL StartServiceThread();
void ServiceCtrlHandler (DWORD controlCode);
int UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
void KillService(void);
void terminateService(int);
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
void ChangeTimeZone(char zone[]);
void UpdateTime(void);
HANDLE killServiceEvent;
DWORD serviceCurrentStatus;
DWORD serviceRunning;
HANDLE threadHandle;
DWORD servicePaused;
TCHAR szErr[256];
char *szServiceName = "TimeHandler";
void main(void)
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{szServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL }
};
// Register the service with the Service Control Manager
StartServiceCtrlDispatcher(serviceTable);
}
void ServiceMain(DWORD argc, LPSTR *argv)
{
int bSuccess;
serviceStatusHandle = RegisterServiceCtrlHandler(szServiceName,
(LPHANDLER_FUNCTION) ServiceCtrlHandler);
if (!serviceStatusHandle)
{
terminateService(GetLastError());
return;
}
bSuccess = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1,
5000);
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
killServiceEvent = CreateEvent(0, 1, 0, 0);
if (!killServiceEvent)
{
terminateService(GetLastError());
return;
}
bSuccess = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2,
1000);
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
ChangeTimeZone(LOCAL_TIME_ZONE);
UpdateTime();
//handle_args( argc, argv );
bSuccess = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 3,
5000);
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
bSuccess = StartServiceThread();
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
serviceCurrentStatus = SERVICE_RUNNING;
bSuccess = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
WaitForSingleObject(killServiceEvent, INFINITE);
terminateService(0);
serviceCurrentStatus = SERVICE_STOPPED;
bSuccess = UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);
return;
}
DWORD ServiceExecutionThread(LPDWORD param)
{
while (serviceRunning)
{
Sleep(SLEEP_TIME);
UpdateTime();
}
return 0;
}
BOOL StartServiceThread()
{
DWORD id;
threadHandle = CreateThread(0, 0,
(LPTHREAD_START_ROUTINE) ServiceExecutionThread, 0, 0, &id);
if (threadHandle != 0)
{
serviceRunning = 1;
return 1;
}
return 0;
// return(threadHandle == 0); //Somehow this line always returns a
non-zero
}
int UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,
DWORD dwWaitHint)
{
int bSuccess;
SERVICE_STATUS serviceStatus;
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = dwCurrentState;
if (dwCurrentState == SERVICE_START_PENDING)
serviceStatus.dwControlsAccepted = 0;
else
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
if (dwServiceSpecificExitCode == 0)
serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
else
serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
serviceStatus.dwCheckPoint = dwCheckPoint;
serviceStatus.dwWaitHint = dwWaitHint;
bSuccess = SetServiceStatus(serviceStatusHandle, &serviceStatus);
if (!bSuccess)
KillService();
return bSuccess;
}
void ServiceCtrlHandler (DWORD controlCode)
{
int bSuccess;
switch (controlCode)
{
case SERVICE_CONTROL_PAUSE:
if (serviceRunning && !servicePaused)
{
bSuccess = UpdateSCMStatus(SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1,
1000);
servicePaused = 1;
SuspendThread(threadHandle);
serviceCurrentStatus = SERVICE_PAUSED;
}
break;
case SERVICE_CONTROL_CONTINUE:
if (serviceRunning && servicePaused)
{
bSuccess = UpdateSCMStatus(SERVICE_CONTINUE_PENDING, NO_ERROR, 0,
1, 1000);
servicePaused = 0;
ResumeThread(threadHandle);
serviceCurrentStatus = SERVICE_RUNNING;
}
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_STOP:
if(RESET_ON_MANUAL_STOP)
{
serviceCurrentStatus = SERVICE_STOP_PENDING;
bSuccess = UpdateSCMStatus(SERVICE_STOP_PENDING,
NO_ERROR, 0, 1, 5000);
KillService();
ChangeTimeZone(FOREIGN_TIME_ZONE); //Remove this and the
next line and the service does not crash
UpdateTime();
return;
}
break;
case SERVICE_CONTROL_SHUTDOWN:
serviceCurrentStatus = SERVICE_STOP_PENDING;
bSuccess = UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1,
5000);
KillService();
ChangeTimeZone(FOREIGN_TIME_ZONE); //Remove this and the
next line and the service does not crash
UpdateTime();
return;
default:
break;
}
UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);
return;
}
void KillService(void)
{
serviceRunning = 0;
SetEvent(killServiceEvent);
}
void terminateService(int iErr)
{
printf("Killing service: Err(%d)\n", iErr);
KillService();
}
void ChangeTimeZone(char zone[])
{
char command[150];
command[0]='\0';
strcat(command,"RunDLL32 shell32.dll,Control_RunDLL
%SystemRoot%\\system32\\TIMEDATE.cpl,,/Z ");
strcat(command,zone);
system(command);
return;
}
void UpdateTime(void)
{
system("w32tm /resync /nowait");
return;
}
</code></noscript>
Oh, on another note, can anyone point out how to allow the time zone to
be specified via command line arguments? I have no experience in
dealing with arguments in C.
Also, how do I make this service self-installable?
Thank you all in advance!
the time zone and forces a resync with a time server on initialisation.
The service will then restore the original time zone upon stopping and
force another resync. I have two versions that does the same thing, the
older one works perfectly but I did not like its structure. Thus I
followed another template, which formed the new version. However, the
new version works, but on shutdown, it finishes changing the time zone
and resyncing and then crashes. If I were to remove the time zone and
resyncing commands, the service terminates successfully.
The following is the old code that works fine:
<noscript><code>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define SLEEP_TIME 1800000
#define RESET_ON_MANUAL_STOP 1
#define LOCAL_TIME_ZONE "Singapore Standard Time"
#define FOREIGN_TIME_ZONE "GMT Standard Time"
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();
void ChangeTimeZone(char zone[])
{
char command[150];
command[0]='\0';
strcat(command,"RunDLL32 shell32.dll,Control_RunDLL
%SystemRoot%\\system32\\TIMEDATE.cpl,,/Z ");
strcat(command,zone);
system(command);
return;
}
void UpdateTime()
{
system("w32tm /resync /nowait");
return;
}
void main()
{
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName = "TimeHandler";
ServiceTable[0].lpServiceProc =
(LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;
// Start the control dispatcher thread for our service
StartServiceCtrlDispatcher(ServiceTable);
}
void ServiceMain(int argc, char** argv)
{
int error;
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
hStatus = RegisterServiceCtrlHandler(
"TimeHandler",
(LPHANDLER_FUNCTION)ControlHandler);
if (hStatus == (SERVICE_STATUS_HANDLE)0)
{
// Registering Control Handler failed
return;
}
// Initialize Service
error = InitService();
if (error)
{
// Initialization failed
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
// We report the running status to SCM.
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);
// The worker loop of a service
while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
{
Sleep(SLEEP_TIME);
UpdateTime();
return;
}
// Service initialization
int InitService()
{
ChangeTimeZone(LOCAL_TIME_ZONE);
UpdateTime();
return 0;
}
// Control handler function
void ControlHandler(DWORD request)
{
switch(request)
{
case SERVICE_CONTROL_STOP:
if(RESET_ON_MANUAL_STOP)
{
ChangeTimeZone(FOREIGN_TIME_ZONE);
UpdateTime();
}
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
case SERVICE_CONTROL_SHUTDOWN:
ChangeTimeZone(FOREIGN_TIME_ZONE);
UpdateTime();
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
return;
default:
break;
}
// Report current status
SetServiceStatus (hStatus, &ServiceStatus);
return;
}
</code></noscript>
The following is the newer code based on another template, which
crashes on termination:
<noscript><code>
#include <stdio.h>
#include <windows.h>
#define SLEEP_TIME 1800000
#define RESET_ON_MANUAL_STOP 1
#define LOCAL_TIME_ZONE "Singapore Standard Time"
#define FOREIGN_TIME_ZONE "GMT Standard Time"
SERVICE_STATUS_HANDLE serviceStatusHandle;
void ServiceMain(DWORD argc, LPSTR *argv);
BOOL StartServiceThread();
void ServiceCtrlHandler (DWORD controlCode);
int UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
void KillService(void);
void terminateService(int);
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
void ChangeTimeZone(char zone[]);
void UpdateTime(void);
HANDLE killServiceEvent;
DWORD serviceCurrentStatus;
DWORD serviceRunning;
HANDLE threadHandle;
DWORD servicePaused;
TCHAR szErr[256];
char *szServiceName = "TimeHandler";
void main(void)
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{szServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL }
};
// Register the service with the Service Control Manager
StartServiceCtrlDispatcher(serviceTable);
}
void ServiceMain(DWORD argc, LPSTR *argv)
{
int bSuccess;
serviceStatusHandle = RegisterServiceCtrlHandler(szServiceName,
(LPHANDLER_FUNCTION) ServiceCtrlHandler);
if (!serviceStatusHandle)
{
terminateService(GetLastError());
return;
}
bSuccess = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1,
5000);
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
killServiceEvent = CreateEvent(0, 1, 0, 0);
if (!killServiceEvent)
{
terminateService(GetLastError());
return;
}
bSuccess = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2,
1000);
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
ChangeTimeZone(LOCAL_TIME_ZONE);
UpdateTime();
//handle_args( argc, argv );
bSuccess = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 3,
5000);
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
bSuccess = StartServiceThread();
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
serviceCurrentStatus = SERVICE_RUNNING;
bSuccess = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
if (!bSuccess)
{
terminateService(GetLastError());
return;
}
WaitForSingleObject(killServiceEvent, INFINITE);
terminateService(0);
serviceCurrentStatus = SERVICE_STOPPED;
bSuccess = UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);
return;
}
DWORD ServiceExecutionThread(LPDWORD param)
{
while (serviceRunning)
{
Sleep(SLEEP_TIME);
UpdateTime();
}
return 0;
}
BOOL StartServiceThread()
{
DWORD id;
threadHandle = CreateThread(0, 0,
(LPTHREAD_START_ROUTINE) ServiceExecutionThread, 0, 0, &id);
if (threadHandle != 0)
{
serviceRunning = 1;
return 1;
}
return 0;
// return(threadHandle == 0); //Somehow this line always returns a
non-zero
}
int UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,
DWORD dwWaitHint)
{
int bSuccess;
SERVICE_STATUS serviceStatus;
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = dwCurrentState;
if (dwCurrentState == SERVICE_START_PENDING)
serviceStatus.dwControlsAccepted = 0;
else
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
if (dwServiceSpecificExitCode == 0)
serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
else
serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
serviceStatus.dwCheckPoint = dwCheckPoint;
serviceStatus.dwWaitHint = dwWaitHint;
bSuccess = SetServiceStatus(serviceStatusHandle, &serviceStatus);
if (!bSuccess)
KillService();
return bSuccess;
}
void ServiceCtrlHandler (DWORD controlCode)
{
int bSuccess;
switch (controlCode)
{
case SERVICE_CONTROL_PAUSE:
if (serviceRunning && !servicePaused)
{
bSuccess = UpdateSCMStatus(SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1,
1000);
servicePaused = 1;
SuspendThread(threadHandle);
serviceCurrentStatus = SERVICE_PAUSED;
}
break;
case SERVICE_CONTROL_CONTINUE:
if (serviceRunning && servicePaused)
{
bSuccess = UpdateSCMStatus(SERVICE_CONTINUE_PENDING, NO_ERROR, 0,
1, 1000);
servicePaused = 0;
ResumeThread(threadHandle);
serviceCurrentStatus = SERVICE_RUNNING;
}
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_STOP:
if(RESET_ON_MANUAL_STOP)
{
serviceCurrentStatus = SERVICE_STOP_PENDING;
bSuccess = UpdateSCMStatus(SERVICE_STOP_PENDING,
NO_ERROR, 0, 1, 5000);
KillService();
ChangeTimeZone(FOREIGN_TIME_ZONE); //Remove this and the
next line and the service does not crash
UpdateTime();
return;
}
break;
case SERVICE_CONTROL_SHUTDOWN:
serviceCurrentStatus = SERVICE_STOP_PENDING;
bSuccess = UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1,
5000);
KillService();
ChangeTimeZone(FOREIGN_TIME_ZONE); //Remove this and the
next line and the service does not crash
UpdateTime();
return;
default:
break;
}
UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);
return;
}
void KillService(void)
{
serviceRunning = 0;
SetEvent(killServiceEvent);
}
void terminateService(int iErr)
{
printf("Killing service: Err(%d)\n", iErr);
KillService();
}
void ChangeTimeZone(char zone[])
{
char command[150];
command[0]='\0';
strcat(command,"RunDLL32 shell32.dll,Control_RunDLL
%SystemRoot%\\system32\\TIMEDATE.cpl,,/Z ");
strcat(command,zone);
system(command);
return;
}
void UpdateTime(void)
{
system("w32tm /resync /nowait");
return;
}
</code></noscript>
Oh, on another note, can anyone point out how to allow the time zone to
be specified via command line arguments? I have no experience in
dealing with arguments in C.
Also, how do I make this service self-installable?
Thank you all in advance!