Writing a service...

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!
 
X

xieliwei

Oh, forgot to mention I'm using Dev-cpp 4.9.9.2, gcc 3.4.2
(mingw-special) and GNU ld 2.15.91 20040904.

Thanks again!
 
F

Flash Gordon

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.

C knows nothing of services or time servers.

void main()

The only return type the C standard defines is int, not void, and using
void provides no advantage that I can see.

int main(void)

<snip>

<OT>I'm not convinced using system() from a service is a good idea</OT>

Other than that, you code is highly system specific. Try on a Windows
programming group.
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.

Look in a real C book (one that does not use void main()) at the allowed
definitions of main. Also play with this

#include <stdio.h>
int main(int argc, char **argv)
{
while (*argv)
puts(*argv++);
return 0;
}

Note that you might not get any arguments, not even a representation of
the program name.
Also, how do I make this service self-installable?

No idea, ask on a Windows group.
 
J

jacob navia

Flash Gordon a écrit :
C knows nothing of services or time servers.

Translation:

"I think that comp.lang.c is only for discussing concepts that appear
explicitely in the published C standard. Since the C standard doesn't
mention anything about time servers I think you should not discuss it
here"
 
W

Walter Roberson

Flash Gordon a écrit :

"I think that comp.lang.c is only for discussing concepts that appear
explicitely in the published C standard. Since the C standard doesn't
mention anything about time servers I think you should not discuss it
here"

If the original poster's code could compile and run on any standard
conforming hosted implementation, there might be *some* room for
the argument that particular algorithms should be discussable.

However, the original poster's code is inherently system specific.
There would be no way to even get it to compile on a non-Windows
system without hacking together extensive headers to define DWORD
and HANDLE and SERVICE_STATUS* and so on.

comp.lang.c is not the place to discuss any random program that happens
to have been written in a C-like language (such as gcc) and which
by choice or by necessity relies upon system-specific extensions.
 
J

Jordan Abel

2006-11-08 <[email protected]>,
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.

If you think you need to sync with the time server after changing time
zones, you don't understand how time zones work on windows
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.

argc and argv. google on those terms.
 
K

Kenneth Brody

jacob said:
Flash Gordon a écrit :

Translation:

"I think that comp.lang.c is only for discussing concepts that appear
explicitely in the published C standard. Since the C standard doesn't
mention anything about time servers I think you should not discuss it
here"

I assume that you disagree with that "translation"? How so?

If the question had been related to the standard C part of the
program, rather than the extremely platform-specific concepts of
things like "Windows services", there wouldn't be any problem.
Witness the recent "question on structs" thread, which seemed at
first to be asking about sockets, sockaddr_in structs, and udp
communications, but in reality was a question about C structs
themselves, and was answered here as being on-topic.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
C

CBFalconer

jacob said:
Flash Gordon a écrit :

Translation:

"I think that comp.lang.c is only for discussing concepts that appear
explicitely in the published C standard. Since the C standard doesn't
mention anything about time servers I think you should not discuss it
here"

Well, at last you understand. Now try to keep that thought in mind.
 
X

xieliwei

Thank you all for explaining to me that this is not the correct place
to post this. I have taken note of your reminders and will move on to a
more system specific group instead.

Thanks.
 
F

Flash Gordon

Thank you all for explaining to me that this is not the correct place
to post this. I have taken note of your reminders and will move on to a
more system specific group instead.

Thanks.

On Nov 9, 7:10 am, CBFalconer <[email protected]> wrote:

<snip>

We (many of us, I believe) appreciate it when people politely accept
redirect. Thank you. However, you should learn that your reply belongs
under (bottom posting) or interspersed with (sometimes called middle
posting) the text you are replying to, not above it (top posting).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,122
Latest member
VinayKumarNevatia_
Top