2020-09-30 17:12:29 +02:00

2019 lines
56 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990-1992 Microsoft Corporation
Module Name:
scapi.c
Abstract:
Contains the Service-related API that are implemented solely in
DLL form. These include:
StartServiceCtrlDispatcherA
StartServiceCtrlDispatcherW
RegisterServiceCtrlHandlerW
RegisterServiceCtrlHandlerA
This file also contains the following local support routines:
ScDispatcherLoop
ScCreateDispatchTableW
ScCreateDispatchTableA
ScCreateThreadStartParms
ScConnectServiceController
ScExpungeMessage
ScGetPipeInput
ScGetDispatchEntry
ScNormalizeCmdLineArgs
ScSendResponse
ScSvcctrlThreadW
ScSvcctrlThreadA
Author:
Dan Lafferty (danl) 09 Apr-1991
Environment:
User Mode -Win32
Revision History:
03-Jun-1996 AnirudhS
ScGetPipeInput: If the message received from the service controller
is not a SERVICE_CONTROL_START message, don't allocate space for the
arguments, since there are none, and since that space never gets freed.
22-Sep-1995 AnirudhS
Return codes from InitializeStatusBinding were not being handled
correctly; success was sometimes reported on failure. Fixed this.
12-Aug-1993 Danl
ScGetDispatchEntry: When the first entry in the table is marked as
OwnProcess, then this function should just return the pointer to the
top of the table. It should ignore the ServiceName. In all cases,
when the service is started as an OWN_PROCESS, only the first entry
in the dispath table should be used.
04-Aug-1992 Danl
When starting a service, always pass the service name as the
first parameter in the argument list.
27-May-1992 JohnRo
RAID 9829: winsvc.h and related file cleanup.
09 Apr-1991 danl
created
--*/
//
// INCLUDES
//
#include <nt.h> // DbgPrint prototype
#include <ntrtl.h> // DbgPrint prototype
#include <rpc.h> // DataTypes and runtime APIs
#include <nturtl.h> // needed for winbase.h
#include <windef.h> // windows types needed for winbase.h
#include <winbase.h> // CreateFile
#include <string.h> // strcmp
#include <stdlib.h> // wide character c runtimes.
#include <tstr.h> // WCSSIZE().
#include <winsvc.h> // public Service Controller Interface.
#include "scbind.h" // InitializeStatusBinding
#include <valid.h> // MAX_SERVICE_NAME_LENGTH
#include <control.h> // CONTROL_PIPE_NAME
#include <scdebug.h> // STATIC
#include <sclib.h> // ScConvertToUnicode
//
// Internal Dispatch Table.
//
typedef union _START_ROUTINE_TYPE {
LPSERVICE_MAIN_FUNCTIONW U; // unicode type
LPSERVICE_MAIN_FUNCTIONA A; // ansi type
} START_ROUTINE_TYPE, *LPSTART_ROUTINE_TYPE;
typedef struct _INTERNAL_DISPATCH_ENTRY {
LPWSTR ServiceName;
START_ROUTINE_TYPE ServiceStartRoutine;
LPHANDLER_FUNCTION ControlHandler;
SERVICE_STATUS_HANDLE StatusHandle;
BOOL OwnProcess;
} INTERNAL_DISPATCH_ENTRY, *LPINTERNAL_DISPATCH_ENTRY;
//
// This structure is passed to the internal
// startup thread which calls the real user
// startup routine with argv, argc parameters.
//
typedef struct _THREAD_STARTUP_PARMSW {
DWORD NumArgs;
LPSERVICE_MAIN_FUNCTIONW ServiceStartRoutine;
LPWSTR VectorTable;
} THREAD_STARTUP_PARMSW, *LPTHREAD_STARTUP_PARMSW;
typedef struct _THREAD_STARTUP_PARMSA {
DWORD NumArgs;
LPSERVICE_MAIN_FUNCTIONA ServiceStartRoutine;
LPSTR VectorTable;
} THREAD_STARTUP_PARMSA, *LPTHREAD_STARTUP_PARMSA;
//
// The following is the amount of time we will wait for the named pipe
// to become available from the Service Controller.
//
#ifdef DEBUG
#define CONTROL_WAIT_PERIOD NMPWAIT_WAIT_FOREVER
#else
#define CONTROL_WAIT_PERIOD 15000 // 15 seconds
#endif
//
// This is the number of times we will continue to loop when pipe read
// failures occur. After this many tries, we cease to read the pipe.
//
#define MAX_RETRY_COUNT 30
//
// Globals
//
LPINTERNAL_DISPATCH_ENTRY DispatchTable=NULL; // table head.
//
// This flag is set to TRUE if the control dispatcher is to support
// ANSI calls. Otherwise the flag is set to FALSE.
//
BOOL AnsiFlag = FALSE;
//
// Internal Functions
//
DWORD
ScCreateDispatchTableW(
IN LPSERVICE_TABLE_ENTRYW UserDispatchTable,
OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
);
DWORD
ScCreateDispatchTableA(
IN LPSERVICE_TABLE_ENTRYA UserDispatchTable,
OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
);
DWORD
ScCreateThreadStartParms(
IN LPCTRL_MSG_HEADER Msg,
IN DWORD NumBytesRead,
OUT LPTHREAD_STARTUP_PARMSW *ThreadParmPtr,
OUT LPBYTE *TempArgPtr,
OUT LPDWORD ArgBytesRemaining
);
VOID
ScDispatcherLoop(
IN HANDLE PipeHandle,
IN LPCTRL_MSG_HEADER Msg,
IN DWORD BufferSize
);
DWORD
ScConnectServiceController (
OUT LPHANDLE pipeHandle
);
VOID
ScExpungeMessage(
IN HANDLE PipeHandle
);
DWORD
ScGetPipeInput (
IN HANDLE PipeHandle,
IN OUT LPCTRL_MSG_HEADER Msg,
IN DWORD BufferSize,
OUT LPTHREAD_STARTUP_PARMSW *ThreadParmPtr
);
DWORD
ScGetDispatchEntry (
OUT LPINTERNAL_DISPATCH_ENTRY *DispatchEntry,
IN LPWSTR ServiceName
);
VOID
ScNormalizeCmdLineArgs(
IN OUT LPCTRL_MSG_HEADER Msg,
IN OUT LPTHREAD_STARTUP_PARMSW ThreadStartupParms
);
VOID
ScSendResponse (
IN HANDLE pipeHandle,
IN DWORD Response
);
DWORD
ScSvcctrlThreadW(
IN LPTHREAD_STARTUP_PARMSW lpThreadStartupParms
);
DWORD
ScSvcctrlThreadA(
IN LPTHREAD_STARTUP_PARMSA lpThreadStartupParms
);
BOOL
WINAPI
StartServiceCtrlDispatcherA (
IN LPSERVICE_TABLE_ENTRYA lpServiceStartTable
)
/*++
Routine Description:
This function provides the ANSI interface for the
StartServiceCtrlDispatcher function.
Arguments:
Return Value:
--*/
{
DWORD status;
NTSTATUS ntstatus;
HANDLE pipeHandle;
LPCTRL_MSG_HEADER msg = NULL;
DWORD bufferSize;
//
// Set the AnsiFlag to indicate that the control dispatcher must support
// ansi function calls only.
//
AnsiFlag = TRUE;
//
// Create an internal DispatchTable.
//
status = ScCreateDispatchTableA(
lpServiceStartTable,
(LPINTERNAL_DISPATCH_ENTRY *)&DispatchTable);
if (status != NO_ERROR) {
SetLastError(status);
return(FALSE);
}
//
// Allocate a buffer big enough to contain at least the control message
// header and a service name. This ensures that, if the message is not
// a START message, it can be read in a single ReadFile.
//
bufferSize = sizeof(CTRL_MSG_HEADER) +
(MAX_SERVICE_NAME_LENGTH+1) * sizeof(WCHAR);
msg = (LPCTRL_MSG_HEADER)LocalAlloc(LMEM_ZEROINIT, (UINT) bufferSize);
if (msg == NULL) {
SCC_LOG1(ERROR,"NetServiceStartCtrlDispatcherA:LocalAlloc failed rc = %d\n",
GetLastError());
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
bufferSize = LocalSize(msg);
if (bufferSize == 0) {
SCC_LOG1(ERROR,"NetServiceStartCtrlDispatcherA:LocalSize failed rc = %d\n",
GetLastError());
ASSERT(0);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
//
// Connect to the Service Controller
//
status = ScConnectServiceController (&pipeHandle);
if (status != NO_ERROR) {
goto CleanExit;
}
//
// Initialize the binding for the status interface (NetServiceStatus).
//
SCC_LOG(TRACE,"Initialize the Status binding\n",0);
ntstatus = InitializeStatusBinding();
if (ntstatus != STATUS_SUCCESS) {
status = RtlNtStatusToDosError(ntstatus);
CloseHandle(pipeHandle);
goto CleanExit;
}
//
// Enter the dispatcher loop where we service control requests until
// all services in the service table have terminated.
//
ScDispatcherLoop (pipeHandle, msg, bufferSize);
CloseHandle(pipeHandle);
CleanExit:
//
// Clean up the dispatch table. Since we created unicode versions
// of all the service names, in ScCreateDispatchTableA, we now need to
// free them.
//
if (DispatchTable != NULL) {
LPINTERNAL_DISPATCH_ENTRY dispatchEntry;
for (dispatchEntry = DispatchTable;
dispatchEntry->ServiceName != NULL;
dispatchEntry++) {
LocalFree(dispatchEntry->ServiceName);
}
LocalFree(DispatchTable);
}
//
// Free the message buffer.
//
if (msg != NULL) {
LocalFree(msg);
}
if (status != NO_ERROR) {
SetLastError(status);
return(FALSE);
}
return(TRUE);
}
BOOL
WINAPI
StartServiceCtrlDispatcherW (
IN LPSERVICE_TABLE_ENTRYW lpServiceStartTable
)
/*++
Routine Description:
This is the Control Dispatcher thread. We do not return from this
function call until the Control Dispatcher is told to shut down.
The Control Dispatcher is responsible for connecting to the Service
Controller's control pipe, and receiving messages from that pipe.
The Control Dispatcher then dispatches the control messages to the
correct control handling routine.
Arguments:
lpServiceStartTable - This is a pointer to the top of a service dispatch
table that the service main process passes in. Each table entry
contains pointers to the ServiceName, and the ServiceStartRotuine.
Return Value:
NO_ERROR - The Control Dispatcher successfully terminated.
ERROR_INVALID_DATA - The specified dispatch table does not contain
entries in the proper format.
ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - The Control Dispatcher
could not connect with the Service Controller.
--*/
{
DWORD status;
NTSTATUS ntStatus;
HANDLE pipeHandle;
LPCTRL_MSG_HEADER msg;
DWORD bufferSize;
//
// Create the Real Dispatch Table
//
try {
status = ScCreateDispatchTableW(lpServiceStartTable, &DispatchTable);
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
if (status != EXCEPTION_ACCESS_VIOLATION) {
SCC_LOG(ERROR,"StartServiceCtrlDispatcherW:Unexpected Exception 0x%lx\n",status);
}
}
if (status != NO_ERROR) {
SetLastError(status);
return(FALSE);
}
//
// Allocate a buffer big enough to contain at least the control message
// header and a service name. This ensures that, if the message is not
// a START message, it can be read in a single ReadFile.
//
bufferSize = sizeof(CTRL_MSG_HEADER) +
(MAX_SERVICE_NAME_LENGTH+1) * sizeof(WCHAR);
msg = (LPCTRL_MSG_HEADER)LocalAlloc(LMEM_ZEROINIT, (UINT) bufferSize);
if (msg == NULL) {
SCC_LOG1(ERROR,"NetServiceStartCtrlDispatcher:LocalAlloc failed rc = %d\n",
GetLastError());
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
bufferSize = LocalSize(msg);
if (bufferSize == 0) {
SCC_LOG1(ERROR,"NetServiceStartCtrlDispatcher:LocalSize failed rc = %d\n",
GetLastError());
ASSERT(0);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
//
// Connect to the Service Controller
//
status = ScConnectServiceController (&pipeHandle);
if (status != NO_ERROR) {
LocalFree(DispatchTable);
LocalFree(msg);
SetLastError(status);
return(FALSE);
}
//
// Initialize the binding for the status interface (NetServiceStatus).
//
SCC_LOG(TRACE,"Initialize the Status binding\n",0);
ntStatus = InitializeStatusBinding();
if (ntStatus != STATUS_SUCCESS) {
status = RtlNtStatusToDosError(ntStatus);
CloseHandle(pipeHandle);
LocalFree(DispatchTable);
LocalFree(msg);
SetLastError(status);
return(FALSE);
}
//
// Enter the dispatcher loop where we service control requests until
// all services in the service table have terminated.
//
ScDispatcherLoop (pipeHandle, msg, bufferSize);
CloseHandle(pipeHandle);
return(TRUE);
}
VOID
ScDispatcherLoop(
IN HANDLE PipeHandle,
IN LPCTRL_MSG_HEADER Msg,
IN DWORD BufferSize
)
/*++
Routine Description:
This is the input loop that the Control Dispatcher stays in through-out
its life. Only two types of events will cause us to leave this loop:
1) The service controller instructed the dispatcher to exit.
2) The dispatcher can no longer communicate with the the
service controller.
Arguments:
PipeHandle: This is a handle to the pipe over which control
requests are received.
Return Value:
none
--*/
{
DWORD status;
DWORD controlStatus;
BOOL continueDispatch;
LPWSTR serviceName;
LPTHREAD_STARTUP_PARMSW threadStartupParmsW;
LPTHREAD_STARTUP_PARMSA threadStartupParmsA;
LPTHREAD_START_ROUTINE threadAddress;
LPVOID threadParms;
LPINTERNAL_DISPATCH_ENTRY dispatchEntry;
DWORD threadId;
HANDLE threadHandle;
DWORD i;
DWORD errorCount = 0;
//
// Input Loop
//
continueDispatch = TRUE;
do {
//
// Wait for input
//
controlStatus = ScGetPipeInput (
PipeHandle,
Msg,
BufferSize,
&threadStartupParmsW);
SCC_LOG(TRACE,"A control message has been received!\n",0);
//
// If we received good input, check to see if we are to shut down
// the ControlDispatcher. If not, then obtain the dispatchEntry
// from the dispatch table.
//
if (controlStatus == NO_ERROR) {
//
// Clear the error count
//
errorCount = 0;
serviceName = (LPWSTR) ((LPBYTE)Msg + Msg->ServiceNameOffset);
if ((serviceName[0] == L'\0') &&
(Msg->OpCode == SERVICE_STOP)) {
//
// The Dispatcher is being asked to shut down.
// (security check not required for this operation)
// Although perhaps it would be a good idea to verify
// that the request came from the Service Controller.
//
controlStatus = NO_ERROR;
continueDispatch = FALSE;
}
else {
dispatchEntry = DispatchTable;
//
// If this is a request to start a service that is in its
// own process, then we expect that there is only one entry
// in the dispatch table. We remember the fact that this
// service is alone in this process.
//
// It should be obvious at this point that we if there
// were other entries in the dispatch table, they will
// be ignored because when SERVICE_CONTROL_START_OWN
// is received. The moral of the story is: "Be sure
// the configuration information in the registry is correct".
//
if (Msg->OpCode == SERVICE_CONTROL_START_OWN) {
dispatchEntry->OwnProcess = TRUE;
}
//
// If the request is for a service that is in it's own
// process, then we don't check the service name.
// Since there is only one dispatch entry, we use the one
// at the top of the table.
//
if (!dispatchEntry->OwnProcess) {
controlStatus = ScGetDispatchEntry(&dispatchEntry, serviceName);
if (controlStatus != NO_ERROR) {
SCC_LOG(TRACE,"Service Name not in Dispatch Table\n",0);
}
}
}
}
else {
if (controlStatus != ERROR_NOT_ENOUGH_MEMORY) {
//
// If an error occured and it is not an out-of-memory error,
// then the pipe read must have failed.
// In this case we Increment the error count.
// When this count reaches the MAX_RETRY_COUNT, then
// the service controller must be gone. We want to log an
// error and notify an administrator. Then go to sleep forever.
// Only a re-boot will solve this problem.
//
// We should be able to report out-of-memory errors back to
// the caller. It should be noted that out-of-memory errors
// do not clear the error count. But they don't add to it
// either.
//
errorCount++;
if (errorCount > MAX_RETRY_COUNT) {
//
// BUGBUG: Add eventlog call here.
//
Sleep(0xffffffff);
}
}
}
//
// Dispatch the request
//
if ((continueDispatch == TRUE) && (controlStatus == NO_ERROR)) {
status = NO_ERROR;
switch(Msg->OpCode) {
case SERVICE_CONTROL_START_SHARE:
case SERVICE_CONTROL_START_OWN:
//
// Update the StatusHandle in the dispatch entry table
//
dispatchEntry->StatusHandle = Msg->StatusHandle;
//
// The Control Dispatcher is to start a service.
// start the new thread.
//
threadStartupParmsW->ServiceStartRoutine =
dispatchEntry->ServiceStartRoutine.U;
threadAddress = (LPTHREAD_START_ROUTINE)ScSvcctrlThreadW;
threadParms = (LPVOID)threadStartupParmsW;
//
// If the service needs to be called with ansi parameters,
// then do the conversion here.
//
if (AnsiFlag) {
threadStartupParmsA =
(LPTHREAD_STARTUP_PARMSA)threadStartupParmsW;
for (i=0; i < threadStartupParmsW->NumArgs; i++) {
if(!ScConvertToAnsi(
*(&threadStartupParmsA->VectorTable + i),
*(&threadStartupParmsW->VectorTable + i))) {
//
// Conversion error occured.
//
SCC_LOG(ERROR,"Could not convert args to ansi\n",0);
status = ERROR_SERVICE_NO_THREAD;
}
}
threadAddress = (LPTHREAD_START_ROUTINE)ScSvcctrlThreadA;
threadParms = (LPVOID)threadStartupParmsA;
}
if (status == NO_ERROR){
//
// Create the new thread
//
threadHandle = CreateThread (
NULL, // Thread Attributes.
0L, // Stack Size
threadAddress, // lpStartAddress
threadParms, // lpParameter
0L, // Creation Flags
&threadId); // lpThreadId
if (threadHandle == (HANDLE) NULL) {
SCC_LOG(ERROR,
"NetServiceStartCtrlDispatcher:CreateThread failed %d\n",
GetLastError());
status = ERROR_SERVICE_NO_THREAD;
}
else {
CloseHandle(threadHandle);
}
}
break;
default:
//
// Call the proper ControlHandler routine.
//
if (dispatchEntry->ControlHandler != NULL) {
try{
#if defined(_X86_)
//
// The Windows NT 3.1 SDK didn't prototype control
// handler functions as WINAPI, so a number of
// existing 3rd-party services have their control
// handler functions built as __cdecl instead. This
// is a workaround.
//
DWORD SaveEdi;
_asm mov SaveEdi, edi;
_asm mov edi, esp; // called function preserves EDI
#endif
dispatchEntry->ControlHandler(
Msg->
OpCode);
#if defined(_X86_)
_asm mov esp, edi;
_asm mov edi, SaveEdi;
#endif
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
if (status != EXCEPTION_ACCESS_VIOLATION) {
SCC_LOG(ERROR,
"StartServiceCtrlDispatcherW:Unexpected Exception 0x%lx\n",
status);
}
status = ERROR_EXCEPTION_IN_SERVICE;
}
}
else {
//
// The control could not be delivered because there
// is no control handling routine registered for this
// service.
//
status = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
}
//
// If status is not good here, then an exception occured
// either because the pointer to the control handling
// routine was bad, or because an exception occured
// inside the control handling routine.
//
// ??EVENTLOG??
//
} // end switch.
//
// Send the status back to the sevice controller.
//
ScSendResponse (PipeHandle, status);
}
else {
//
// The controlStatus indicates failure, we always want to try
// to send the status back to the Service Controller.
//
ScSendResponse (PipeHandle, controlStatus);
switch (controlStatus) {
case ERROR_SERVICE_DOES_NOT_EXIST:
case ERROR_SERVICE_NO_THREAD:
//
// The Service Name is not in this .exe's dispatch table.
// Or a thread for a new service couldn't be created.
// ignore it. The Service Controller will tell us to
// shut down if necessary.
//
controlStatus = NO_ERROR;
break;
default:
//
// If the error is not specifically recognized, continue.
//
controlStatus = NO_ERROR;
break;
}
}
}
while (continueDispatch == TRUE);
return;
}
SERVICE_STATUS_HANDLE
WINAPI
RegisterServiceCtrlHandlerW (
IN LPCWSTR ServiceName,
IN LPHANDLER_FUNCTION ControlHandler
)
/*++
Routine Description:
This function enters a pointer to a control handling routine and a
pointer to a security descriptor into the Control Dispatcher's
dispatch table.
Arguments:
ServiceName - This is a pointer to the Service Name string.
ControlHandler - This is a pointer to the service's control handling
routine.
Return Value:
This function returns a handle to the service that is to be used in
subsequent calls to SetServiceStatus. If the return value is NULL,
an error has occured, and GetLastError can be used to obtain the
error value. Possible values for error are:
NO_ERROR - If the operation was successful.
ERROR_INVALID_PARAMETER - The pointer to the control handler function
is NULL.
ERROR_INVALID_DATA -
ERROR_SERVICE_DOES_NOT_EXIST - The serviceName could not be found in
the dispatch table. This indicates that the configuration database
says the serice is in this process, but the service name doesn't
exist in the dispatch table.
--*/
{
DWORD status;
LPINTERNAL_DISPATCH_ENTRY dispatchEntry;
//
// Find the service in the dispatch table.
//
dispatchEntry = DispatchTable;
try {
status = ScGetDispatchEntry(&dispatchEntry, (LPWSTR) ServiceName);
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
if (status != EXCEPTION_ACCESS_VIOLATION) {
SCC_LOG(ERROR,"RegisterServiceCtrlHandlerW:Unexpected Exception 0x%lx\n",status);
}
}
if(status != NO_ERROR) {
SCC_LOG(ERROR,
"RegisterServiceCtrlHandlerW: can't find dispatch entry\n",0);
SetLastError(status);
return(0L);
}
//
// Insert the ControlHandler and SecurityDescriptor pointers
//
if (ControlHandler == NULL) {
SCC_LOG(ERROR,
"RegisterServiceCtrlHandlerW: Ptr to ctrlhandler is NULL\n",0);
SetLastError(ERROR_INVALID_PARAMETER);
return(0L);
}
//
// Insert the entries into the table
//
dispatchEntry->ControlHandler = ControlHandler;
return(dispatchEntry->StatusHandle);
}
SERVICE_STATUS_HANDLE
WINAPI
RegisterServiceCtrlHandlerA (
IN LPCSTR ServiceName,
IN LPHANDLER_FUNCTION ControlHandler
)
/*++
Routine Description:
This is the ansi entry point for RegisterServiceCtrlHandler.
Arguments:
Return Value:
--*/
{
LPWSTR ServiceNameW;
SERVICE_STATUS_HANDLE statusHandle;
if(!ScConvertToUnicode(&ServiceNameW, ServiceName)) {
//
// The only thing that could happen to cause this to fail is
// a failure in LocalAlloc. (or else the ansi string is garbage).
//
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(0L);
}
statusHandle = RegisterServiceCtrlHandlerW( ServiceNameW, ControlHandler);
LocalFree(ServiceNameW);
return(statusHandle);
}
DWORD
ScCreateDispatchTableW(
IN LPSERVICE_TABLE_ENTRYW lpServiceStartTable,
OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
)
/*++
Routine Description:
This routine allocates space for the Control Dispatchers Dispatch Table.
It also initializes the table with the data that the service main
routine passed in with the lpServiceStartTable parameter.
This routine expects that pointers in the user's dispatch table point
to valid information. And that that information will stay valid and
fixed through out the life of the Control Dispatcher. In otherwords,
the ServiceName string better not move or get cleared.
Arguments:
lpServiceStartTable - This is a pointer to the first entry in the
dispatch table that the service's main routine passed in .
DispatchTablePtr - This is a pointer to the location where the
Service Controller's dispatch table is to be stored.
Return Value:
NO_ERROR - The operation was successful.
ERROR_NOT_ENOUGH_MEMORY - The memory allocation failed.
ERROR_INVALID_PARAMETER - There are no entries in the dispatch table.
--*/
{
DWORD numEntries;
LPINTERNAL_DISPATCH_ENTRY dispatchTable;
LPSERVICE_TABLE_ENTRYW entryPtr;
//
// Count the number of entries in the user dispatch table
//
numEntries = 0;
entryPtr = lpServiceStartTable;
while (entryPtr->lpServiceName != NULL) {
numEntries++;
entryPtr++;
}
if (numEntries == 0) {
SCC_LOG(ERROR,"ScCreateDispatchTable:No entries in Dispatch table!\n",0);
return(ERROR_INVALID_PARAMETER);
}
//
// Allocate space for the Control Dispatcher's Dispatch Table
//
dispatchTable = (LPINTERNAL_DISPATCH_ENTRY)LocalAlloc(LMEM_ZEROINIT,
sizeof(INTERNAL_DISPATCH_ENTRY) * (numEntries + 1));
if (dispatchTable == NULL) {
SCC_LOG(ERROR,"ScCreateDispatchTable: Local Alloc failed rc = %d\n",
GetLastError());
return (ERROR_NOT_ENOUGH_MEMORY);
}
//
// Move user dispatch info into the Control Dispatcher's table.
//
*DispatchTablePtr = dispatchTable;
entryPtr = lpServiceStartTable;
while (entryPtr->lpServiceName != NULL) {
dispatchTable->ServiceName = entryPtr->lpServiceName;
dispatchTable->ServiceStartRoutine.U= entryPtr->lpServiceProc;
dispatchTable->ControlHandler = NULL;
dispatchTable->StatusHandle = (SERVICE_STATUS_HANDLE)0;
dispatchTable->OwnProcess = FALSE;
entryPtr++;
dispatchTable++;
}
return (NO_ERROR);
}
DWORD
ScCreateDispatchTableA(
IN LPSERVICE_TABLE_ENTRYA lpServiceStartTable,
OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
)
/*++
Routine Description:
This routine allocates space for the Control Dispatchers Dispatch Table.
It also initializes the table with the data that the service main
routine passed in with the lpServiceStartTable parameter.
This routine expects that pointers in the user's dispatch table point
to valid information. And that that information will stay valid and
fixed through out the life of the Control Dispatcher. In otherwords,
the ServiceName string better not move or get cleared.
Arguments:
lpServiceStartTable - This is a pointer to the first entry in the
dispatch table that the service's main routine passed in .
DispatchTablePtr - This is a pointer to the location where the
Service Controller's dispatch table is to be stored.
Return Value:
NO_ERROR - The operation was successful.
ERROR_NOT_ENOUGH_MEMORY - The memory allocation failed.
ERROR_INVALID_PARAMETER - There are no entries in the dispatch table.
--*/
{
DWORD numEntries;
DWORD status = NO_ERROR;
LPINTERNAL_DISPATCH_ENTRY dispatchTable;
LPSERVICE_TABLE_ENTRYA entryPtr;
//
// Count the number of entries in the user dispatch table
//
numEntries = 0;
entryPtr = lpServiceStartTable;
while (entryPtr->lpServiceName != NULL) {
numEntries++;
entryPtr++;
}
if (numEntries == 0) {
SCC_LOG(ERROR,"ScCreateDispatchTable:No entries in Dispatch table!\n",0);
return(ERROR_INVALID_PARAMETER);
}
//
// Allocate space for the Control Dispatcher's Dispatch Table
//
dispatchTable = (LPINTERNAL_DISPATCH_ENTRY)LocalAlloc(LMEM_ZEROINIT,
sizeof(INTERNAL_DISPATCH_ENTRY) * (numEntries + 1));
if (dispatchTable == NULL) {
SCC_LOG(ERROR,"ScCreateDispatchTableA: Local Alloc failed rc = %d\n",
GetLastError());
return (ERROR_NOT_ENOUGH_MEMORY);
}
//
// Move user dispatch info into the Control Dispatcher's table.
//
*DispatchTablePtr = dispatchTable;
entryPtr = lpServiceStartTable;
while (entryPtr->lpServiceName != NULL) {
//
// Convert the service name to unicode
//
try {
if (!ScConvertToUnicode(
&(dispatchTable->ServiceName),
entryPtr->lpServiceName)) {
//
// The convert failed.
//
SCC_LOG(ERROR,"ScCreateDispatcherTableA:ScConvertToUnicode failed\n",0);
//
// This is the only reason for failure that I can think of.
//
status = ERROR_NOT_ENOUGH_MEMORY;
}
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
if (status != EXCEPTION_ACCESS_VIOLATION) {
SCC_LOG(ERROR,
"ScCreateDispatchTableA: Unexpected Exception 0x%lx\n",status);
}
}
if (status != NO_ERROR) {
//
// If an error occured, free up the allocated resources.
//
dispatchTable = *DispatchTablePtr;
while (dispatchTable->ServiceName != NULL) {
LocalFree(dispatchTable->ServiceName);
dispatchTable++;
}
LocalFree(*DispatchTablePtr);
return(status);
}
//
// Fill in the rest of the dispatch entry.
//
dispatchTable->ServiceStartRoutine.A= entryPtr->lpServiceProc;
dispatchTable->ControlHandler = NULL;
dispatchTable->StatusHandle = (SERVICE_STATUS_HANDLE)0;
dispatchTable->OwnProcess = FALSE;
entryPtr++;
dispatchTable++;
}
return (NO_ERROR);
}
DWORD
ScCreateThreadStartParms(
IN LPCTRL_MSG_HEADER Msg,
IN DWORD NumBytesRead,
OUT LPTHREAD_STARTUP_PARMSW *ThreadParmPtr,
OUT LPBYTE *TempArgPtr,
OUT LPDWORD ArgBytesRemaining
)
/*++
Routine Description:
This routine calculates the number of bytes needed for the services
thread startup parameters by using the arg count information in the
message header. The startup parameter structure is allocated and
as many bytes of argument information as has been captured so far
is placed into the buffer. A second read of the pipe may be necessary
to obtain the remaining bytes of argument information.
NOTE: This function allocates enough space in the startup parameter
buffer for the service name and pointer as well as the rest of the
arguments. However, it does not put the service name into the argument
list. This is because it may take two calls to this routine to
get all the argument information. We can't insert the service name
string until we have all the rest of the argument data.
[serviceNamePtr][argv1]argv2]...[argv1Str][argv2Str]...[serviceNameStr]
Arguments:
Msg - A pointer to the pipe message header.
NumBytesRead - The number of bytes read in the first pipe read.
ThreadParmPtr - A pointer to a location where the pointer to the
thread startup parameter structure is to be placed.
TempArgPtr - A location that will contain the pointer to where
more argument data can be placed by a second read of the pipe.
ArgBytesRemaining - Returns with a count of the number of argument bytes
that remain to be read from the pipe.
Return Value:
NO_ERROR - If the operation was successful.
ERROR_NOT_ENOUGH_MEMORY - If the memory allocation was unsuccessful.
Note:
--*/
{
DWORD nameSize; // num bytes in ServiceName.
DWORD parmBufSize; // num bytes for threadStartupParm buffer
DWORD argBytesCaptured; // number of arg bytes in first read.
LPTHREAD_STARTUP_PARMSW threadStartupParms;
SCC_LOG(TRACE,"ScCreateThreadStartParms: Get Cmd Line Args from pipe\n",0);
//
// Note: Here we assume that the service name was read into the buffer
// in its entirety.
//
nameSize = WCSSIZE((LPWSTR)((LPBYTE)Msg + Msg->ServiceNameOffset));
//
// Calculate the size of buffer needed. This will consist of an
// THREAD_STARTUP_PARMS structure, plus the service name and a pointer
// for it, plus the rest of the arg info sent in
// the message (We are wasting 4 bytes here since the first pointer in
// the vector table is accounted for twice - but what the heck!).
//
parmBufSize = Msg->Count -
sizeof(CTRL_MSG_HEADER) +
sizeof(THREAD_STARTUP_PARMSW) +
sizeof(LPWSTR);
//
// Allocate the memory for the thread parameter list.
//
threadStartupParms = (LPTHREAD_STARTUP_PARMSW)LocalAlloc(
LMEM_ZEROINIT,
(UINT)parmBufSize);
if (threadStartupParms == NULL) {
SCC_LOG(ERROR,"ScCreateThreadStartParms: LocalAlloc failed rc=%d\n",
GetLastError());
return(ERROR_NOT_ENOUGH_MEMORY);
}
argBytesCaptured = NumBytesRead - sizeof(CTRL_MSG_HEADER) - nameSize;
*ArgBytesRemaining = Msg->Count - NumBytesRead;
*TempArgPtr = (LPBYTE)&threadStartupParms->VectorTable;
//
// Leave the first vector location blank for the service name
// pointer.
//
(*TempArgPtr) += sizeof(LPWSTR);
if (Msg->NumCmdArgs != 0) {
//
// adjust argBytesCaptured to remove any extra bytes that are there
// for alignment. It should be noted that the initial buffer size is
// always large enough contain all alignment bytes. Therefore,
// the adjusted argBytesCaptured can never be a negative number.
//
// If a name that is not in the dispatch table is passed in, it could
// be larger than our buffer. This could cause argBytesCaptured to
// become negative. However it should fail safe anyway since the
// name simply won't be recognized and an error will be returned.
//
argBytesCaptured -= (Msg->ArgvOffset - Msg->ServiceNameOffset - nameSize);
//
// Copy any portion of the command arg info from the first read
// into the buffer that is to be used for the second read.
//
if (argBytesCaptured > 0) {
memcpy(
*TempArgPtr,
(LPBYTE)Msg + Msg->ArgvOffset,
(UINT)argBytesCaptured);
*TempArgPtr += argBytesCaptured;
}
}
else {
*TempArgPtr = NULL;
}
*ThreadParmPtr = threadStartupParms;
return (NO_ERROR);
}
DWORD
ScConnectServiceController (
OUT LPHANDLE PipeHandle
)
/*++
Routine Description:
This function connects to the Service Controller Pipe.
Arguments:
PipeHandle - This is a pointer to the location where the PipeHandle
is to be placed.
Return Value:
NO_ERROR - if the operation was successful.
ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - if we failed to connect.
--*/
{
BOOL status;
DWORD apiStatus;
DWORD response;
DWORD pipeMode;
DWORD numBytesWritten;
status = WaitNamedPipeW (
CONTROL_PIPE_NAME,
CONTROL_WAIT_PERIOD);
if (status != TRUE) {
SCC_LOG(ERROR,"ScConnectServiceController:WaitNamedPipe failed rc = %d\n",
GetLastError());
}
SCC_LOG(TRACE,"ScConnectServiceController:WaitNamedPipe success\n",0);
*PipeHandle = CreateFileW(
CONTROL_PIPE_NAME, // lpFileName
GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
NULL, // lpSecurityAttributes
OPEN_EXISTING, // dwCreationDisposition
FILE_ATTRIBUTE_NORMAL, // dwFileAttributes
0L); // hTemplateFile
if (*PipeHandle == (HANDLE)-1) {
SCC_LOG(ERROR,"ScConnectServiceController:CreateFile failed rc = %d\n",
GetLastError());
return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
}
SCC_LOG(TRACE,"ScConnectServiceController:CreateFile success\n",0);
//
// Set pipe mode
//
pipeMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
status = SetNamedPipeHandleState (
*PipeHandle,
&pipeMode,
NULL,
NULL);
if (status != TRUE) {
SCC_LOG(ERROR,"ScConnectServiceController:SetNamedPipeHandleState failed rc = %d\n",
GetLastError());
return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
}
else {
SCC_LOG(TRACE,
"ScConnectServiceController SetNamedPipeHandleState Success\n",0);
}
//
// Send initial status - This is the process Id for the service process.
//
response = GetCurrentProcessId();
apiStatus = WriteFile (
*PipeHandle,
&response,
sizeof(response),
&numBytesWritten,
NULL);
if (apiStatus != TRUE) {
//
// If this fails, there is a chance that the pipe is still in good
// shape. So we just go on.
//
// ??EVENTLOG??
//
SCC_LOG(ERROR,"ScConnectServiceController: WriteFile failed, rc= %d\n", GetLastError());
}
else {
SCC_LOG(TRACE,
"ScConnectServiceController: WriteFile success, bytes Written= %d\n",
numBytesWritten);
}
return(NO_ERROR);
}
VOID
ScExpungeMessage(
IN HANDLE PipeHandle
)
/*++
Routine Description:
This routine cleans the remaining portion of a message out of the pipe.
It is called in response to an unsuccessful attempt to allocate the
correct buffer size from the heap. In this routine a small buffer is
allocated on the stack, and successive reads are made until a status
other than ERROR_MORE_DATA is received.
Arguments:
PipeHandle - This is a handle to the pipe in which the message resides.
Return Value:
none - If this operation fails, there is not much I can do about
the data in the pipe.
--*/
{
#define EXPUNGE_BUF_SIZE 100
DWORD status;
DWORD numBytesRead=0;
BYTE msg[EXPUNGE_BUF_SIZE];
do {
status = ReadFile (
PipeHandle,
msg,
EXPUNGE_BUF_SIZE,
&numBytesRead,
NULL);
}
while( status == ERROR_MORE_DATA);
}
DWORD
ScGetPipeInput (
IN HANDLE PipeHandle,
IN OUT LPCTRL_MSG_HEADER Msg,
IN DWORD BufferSize,
OUT LPTHREAD_STARTUP_PARMSW *ThreadParmPtr
)
/*++
Routine Description:
This routine reads a control message from the pipe and places it into
a message buffer. This routine also allocates a structure for
the service thread information. This structure will eventually
contain everything that is needed to invoke the service startup
routine in the context of a new thread. Items contained in the
structure are:
1) The pointer to the startup routine,
2) The number of arguments, and
3) The table of vectors to the arguments.
Since this routine has knowledge about the buffer size needed for
the arguments, the allocation is done here.
Arguments:
PipeHandle - This is the handle for the pipe that is to be read.
Msg - This is a pointer to a buffer where the data is to be
placed.
BufferSize - This is the size (in bytes) of the buffer that data is to
be placed in.
CmdArgPtr - This is the location where the pointer to the Command
arg pointers is to be placed upon return. If there are no Command
Args, then this will return a NULL.
NumArgs - This is the location where the number of CmdArgs is to be
placed. If there are no command args, this will return a 0.
Return Value:
NO_ERROR - if the operation was successful.
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to create a large
enough buffer for the command line arguments.
ERROR_INVALID_DATA - This is returned if we did not receive a complete
CTRL_MESSAGE_HEADER on the first read.
Any error that ReadFile might return could be returned by this function.
(We may want to return something more specific like ERROR_READ_FAULT)
--*/
{
DWORD status;
BOOL readStatus;
DWORD numBytesRead=0;
DWORD argBytesRemaining;
LPBYTE tempArgPtr;
*ThreadParmPtr = NULL;
//
// Read the header and name string from the pipe.
// NOTE: The number of bytes for the name string is determined by
// the longest service name in the service process. If the actual
// string read is shorter, then the beginning of the command arg
// data may be read with this read.
// Also note: The buffer is large enough to accommodate the longest
// permissible service name.
//
readStatus = ReadFile (
PipeHandle,
Msg,
BufferSize,
&numBytesRead,
NULL);
SCC_LOG(TRACE,"ScGetPipeInput:ReadFile buffer size = %ld\n",BufferSize);
SCC_LOG(TRACE,"ScGetPipeInput:ReadFile numBytesRead = %ld\n",numBytesRead);
if ((readStatus == TRUE) && (numBytesRead > sizeof(CTRL_MSG_HEADER))) {
//
// The Read File read the complete message in one read. So we
// can return with the data.
//
SCC_LOG(TRACE,"ScGetPipeInput:Success!\n",0);
if (Msg->OpCode == SERVICE_CONTROL_START_OWN ||
Msg->OpCode == SERVICE_CONTROL_START_SHARE) {
//
// Allocate storage for thread startup parameters.
// Place any arguments that have been captured into the
// structure.
//
status = ScCreateThreadStartParms(
Msg,
numBytesRead,
ThreadParmPtr,
&tempArgPtr,
&argBytesRemaining);
if (status != NO_ERROR) {
return(status);
}
//
// Change the offsets back into pointers.
//
ScNormalizeCmdLineArgs(Msg, *ThreadParmPtr);
}
else {
ASSERT(Msg->NumCmdArgs == 0);
*ThreadParmPtr = NULL;
}
return(NO_ERROR);
}
else {
//
// An error was returned from ReadFile. ERROR_MORE_DATA
// means that we need to read some arguments from the buffer.
// Any other error is unexpected, and generates an internal error.
//
if (readStatus != TRUE) {
status = GetLastError();
if (status != ERROR_MORE_DATA) {
SCC_LOG(ERROR,"ScGetPipeInput:Unexpected return code, rc= %ld\n",
status);
return(status);
}
}
else {
//
// The read was successful, but we didn't get a complete
// CTRL_MESSAGE_HEADER.
//
return(ERROR_INVALID_DATA);
}
}
//
// We must have received an ERROR_MORE_DATA to go down this
// path. This means that the message contains more data. Namely,
// command line arguments must be present. Therefore, the pipe must
// be read again. Since the header indicates how many bytes are
// needed, we will allocate a buffer large enough to hold all the
// command args.
//
// If a portion of the command line args was read in the first read,
// they will be put in this new buffer. That is so that all the
// command line arg info is in one place.
//
//
// Allocate storage for thread startup parameters.
// Place any arguments that have been captured into the
// structure.
//
status = ScCreateThreadStartParms(
Msg,
numBytesRead,
ThreadParmPtr,
&tempArgPtr,
&argBytesRemaining);
if (status != NO_ERROR) {
ScExpungeMessage(PipeHandle);
return(status);
}
readStatus = ReadFile (
PipeHandle,
tempArgPtr,
argBytesRemaining,
&numBytesRead,
NULL);
if ((readStatus != TRUE) || (numBytesRead < argBytesRemaining)) {
if (readStatus != TRUE) {
status = GetLastError();
SCC_LOG(ERROR,"ScGetPipeInput: ReadFile error (2nd read), rc = %ld\n",
status);
}
else {
status = ERROR_BAD_LENGTH;
}
SCC_LOG(ERROR,"ScGetPipeInput: ReadFile read:%d,", numBytesRead);
SCC_LOG(ERROR," expected:%d\n", argBytesRemaining);
LocalFree(*ThreadParmPtr);
return(status);
}
//
// Change the offsets back into pointers.
//
ScNormalizeCmdLineArgs(Msg, *ThreadParmPtr);
return(NO_ERROR);
}
DWORD
ScGetDispatchEntry (
IN OUT LPINTERNAL_DISPATCH_ENTRY *DispatchEntryPtr,
IN LPWSTR ServiceName
)
/*++
Routine Description:
Finds an entry in the Dispatch Table for a particular service which
is identified by a service name string.
Arguments:
DispatchEntryPtr - As an input, the is a location where a pointer to
the top of the DispatchTable is placed. On return, this is the
location where the pointer to the specific dispatch entry is to
be placed. This is an opaque pointer because it could be either
ansi or unicode depending on the operational state of the dispatcher.
ServiceName - This is a pointer to the service name string.
Return Value:
NO_ERROR - The operation was successful.
ERROR_SERVICE_DOES_NOT_EXIST - The serviceName could not be found in
the dispatch table. This indicates that the configuration database
says the serice is in this process, but the service name doesn't
exist in the dispatch table.
--*/
{
LPINTERNAL_DISPATCH_ENTRY entryPtr;
DWORD found = FALSE;
//
// BUGBUG: I need to create a lock for the dispatch table. I must
// get the lock before updating the table.
//
entryPtr = *DispatchEntryPtr;
if (entryPtr->OwnProcess){
return(NO_ERROR);
}
while (entryPtr->ServiceName != NULL) {
if (_wcsicmp(entryPtr->ServiceName, ServiceName) == 0) {
found = TRUE;
break;
}
entryPtr++;
}
if (found) {
*DispatchEntryPtr = entryPtr;
}
else {
SCC_LOG(ERROR,"ScGetDispatchEntry: DispatchEntry not found\n",0);
SCC_LOG(ERROR,"There is a configuration error - the service is not in this .exe file\n",0);
return(ERROR_SERVICE_DOES_NOT_EXIST);
}
return(NO_ERROR);
}
VOID
ScNormalizeCmdLineArgs(
IN OUT LPCTRL_MSG_HEADER Msg,
IN OUT LPTHREAD_STARTUP_PARMSW ThreadStartupParms
)
/*++
Routine Description:
Normalizes the command line argument information that came across in
the pipe. The argument information is stored in a buffer that consists
of an array of string pointers followed by the strings themselves.
However, in the pipe, the pointers are replaced with offsets. This
routine transforms the offsets into real pointers.
This routine also puts the service name into the array of argument
vectors, and adds the service name string to the end of the
buffer (space has already been allocated for it).
Arguments:
Msg - This is a pointer to the Message. Useful information from this
includes the NumCmdArgs and the service name.
ThreadStartupParms - A pointer to the thread startup parameter structure.
Return Value:
none.
--*/
{
DWORD i;
LPWSTR *argv;
DWORD numCmdArgs;
LPWSTR *serviceNameVector;
LPWSTR serviceNamePtr;
numCmdArgs = Msg->NumCmdArgs;
argv = &(ThreadStartupParms->VectorTable);
//
// Save the first argv for the service name.
//
serviceNameVector = argv;
argv++;
//
// Normalize the Command Line Argument information by replacing
// offsets in buffer with pointers.
//
// NOTE: The elaborate casting that takes place here is because we
// are taking some (pointer sized) offsets, and turning them back
// into pointers to strings. The offsets are in bytes, and are
// relative to the beginning of the vector table which contains
// pointers to the various command line arg strings.
//
for (i=0; i<(numCmdArgs); i++) {
argv[i] = (LPWSTR)((LPBYTE)argv + (DWORD)argv[i]);
}
//
// If we are starting a service, then we need to add the service name
// to the argument vectors.
//
if ((Msg->OpCode == SERVICE_CONTROL_START_SHARE) ||
(Msg->OpCode == SERVICE_CONTROL_START_OWN)) {
numCmdArgs++;
if (numCmdArgs > 1) {
//
// Find the location for the service name string by finding
// the pointer to the last argument adding its string length
// to it.
//
serviceNamePtr = argv[i-1];
serviceNamePtr += (wcslen(serviceNamePtr) + 1);
}
else {
serviceNamePtr = (LPWSTR)argv;
}
wcscpy(serviceNamePtr, (LPWSTR) ((LPBYTE)Msg + Msg->ServiceNameOffset));
*serviceNameVector = serviceNamePtr;
}
ThreadStartupParms->NumArgs = numCmdArgs;
}
VOID
ScSendResponse (
IN HANDLE PipeHandle,
IN DWORD Response
)
/*++
Routine Description:
This routine sends a status response to the Service Controller's pipe.
Arguments:
Response - This is the status message that is to be sent.
Return Value:
none.
--*/
{
DWORD numBytesWritten;
DWORD status;
status = WriteFile (
PipeHandle,
&Response,
sizeof(Response),
&numBytesWritten,
NULL);
if (status != TRUE) {
SCC_LOG(ERROR,"ScSendResponse: WriteFile failed, rc= %d\n",
GetLastError());
//
// BUGBUG: I probably need to do more for an error condition here.
// If the response doesn't make it back to the Service Controller,
// we may have lost the connection. Perhaps I need to re-open it.
// Check into this...
//
}
}
DWORD
ScSvcctrlThreadW(
IN LPTHREAD_STARTUP_PARMSW lpThreadStartupParms
)
/*++
Routine Description:
This is the thread for the newly started service. This code
calls the service's main thread with parameters from the
ThreadStartupParms structure.
NOTE: The first item in the argument vector table is the pointer to
the service registry path string.
Arguments:
lpThreadStartupParms - This is a pointer to the ThreadStartupParms
structure. (This is a unicode structure);
Return Value:
--*/
{
//
// Call the Service's Main Routine.
//
((LPSERVICE_MAIN_FUNCTIONW)lpThreadStartupParms->ServiceStartRoutine) (
lpThreadStartupParms->NumArgs,
&lpThreadStartupParms->VectorTable);
LocalFree(lpThreadStartupParms);
ExitThread(0);
return(0);
}
DWORD
ScSvcctrlThreadA(
IN LPTHREAD_STARTUP_PARMSA lpThreadStartupParms
)
/*++
Routine Description:
This is the thread for the newly started service. This code
calls the service's main thread with parameters from the
ThreadStartupParms structure.
NOTE: The first item in the argument vector table is the pointer to
the service registry path string.
Arguments:
lpThreadStartupParms - This is a pointer to the ThreadStartupParms
structure. (This is a unicode structure);
Return Value:
--*/
{
//
// Call the Service's Main Routine.
//
// NOTE: The first item in the argument vector table is the pointer to
// the service registry path string.
//
((LPSERVICE_MAIN_FUNCTIONA)lpThreadStartupParms->ServiceStartRoutine) (
lpThreadStartupParms->NumArgs,
&lpThreadStartupParms->VectorTable);
LocalFree(lpThreadStartupParms);
ExitThread(0);
return(0);
}