// --winwrap.c------------------------------------------------------------------
//
// Windows NT service (Windows application) shell for EDK applications.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------
#include "edk.h"
#include <stdlib.h>
static struct _tparam
{
LPLONG lplStartThreads;
LPLONG lplStopThreads;
LPTHREAD_START_ROUTINE lpStartAddress;
LPVOID lpParameter;
};
#include "winwrap.chk"
#define DEFAULT_WAIT_HINT 30000
#define SERVICE_NAME "WINWRAP"
#define WAIT_DESTROY 120000
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static BOOL fIsService = TRUE;
static DWORD dwStartWaitHint = DEFAULT_WAIT_HINT;
static DWORD dwStartCheckPoint = 0;
static DWORD dwPauseWaitHint = DEFAULT_WAIT_HINT;
static DWORD dwPauseCheckPoint = 0;
static DWORD dwContinueWaitHint = DEFAULT_WAIT_HINT;
static DWORD dwContinueCheckPoint = 0;
static DWORD dwStopWaitHint = DEFAULT_WAIT_HINT;
static DWORD dwStopCheckPoint = 0;
static HINSTANCE hInst = NULL;
static HANDLE hServiceStopEvent = NULL;
static HANDLE hServicePauseEvent = NULL;
static HANDLE hServiceContinueEvent = NULL;
static HANDLE hStopConfirmEvent = NULL;
static SERVICE_STATUS ssStatus = {0};
static SERVICE_STATUS_HANDLE sshStatusHandle = 0;
static HWND hAppWnd = NULL;
static DWORD dwWorkerTID = 0;
static HANDLE hWorkerThread = NULL;
static DWORD dwShutdownTID = 0;
static HANDLE hShutdownThread = NULL;
static DWORD dwServiceTID = 0;
static HANDLE hServiceThread = NULL;
// -----------------------------------------------------------------------------
static DWORD dwServiceArgc = 0;
static LPSTR *lpszServiceArgv = NULL;
static CHAR szServiceName[MAX_SERVICE_NAME_LENGTH+1] = {0};
static DWORD dwSvcWin32ExitCode = 0;
static DWORD dwSvcServiceSpecificExitCode = 0;
static struct _WinMainContext
{
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpszCmdLine;
int nCmdShow;
} WinMainContext;
//$--FIsService-----------------------------------------------------------------
// Returns TRUE if the application is running as an NT service.
// -----------------------------------------------------------------------------
BOOL FIsService( // RETURNS: TRUE if service
VOID) // no arguments
{
return(fIsService);
}
//$--GetServiceStopEvent-------------------------------------------------------
// Returns the handle for the service stop event.
// -----------------------------------------------------------------------------
HANDLE GetServiceStopEvent( // RETURNS: handle to stop event
VOID) // no arguments
{
return(hServiceStopEvent);
}
//$--GetServiceInstance--------------------------------------------------------
// Returns the handle for the service instance.
// -----------------------------------------------------------------------------
HANDLE GetServiceInstance( // RETURNS: handle to service instance
VOID) // no arguments
{
return(hInst);
}
//$--FnCreateThread-------------------------------------------------------------
// Wrapper for CreateThread() function.
// -----------------------------------------------------------------------------
static VOID FnCreateThread( // RETURNS: nothing
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;
HGLOBAL hglobal = NULL;
struct _tparam *lpContext = NULL;
DEBUGPRIVATE("FnCreateThread()\n");
hr = CHK_HrFnCreateThread(
lpParameter);
if(FAILED(hr))
return;
lpContext = (struct _tparam *)lpParameter;
InterlockedIncrement(lpContext->lplStartThreads);
__try
{
(*(lpContext->lpStartAddress))(lpContext->lpParameter);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(_exception_code());
hr = HR_LOG(E_FAIL);
}
InterlockedIncrement(lpContext->lplStopThreads);
__try
{
hglobal = GlobalFree(lpContext);
if(hglobal != NULL)
{
hr = HR_LOG(E_FAIL);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(_exception_code());
hr = HR_LOG(E_FAIL);
}
}
//$--HServiceCreateThread-----------------------------------------------------------
// Create a wrapped thread.
// -----------------------------------------------------------------------------
HANDLE HServiceCreateThread( // RETURNS: handle
IN LPLONG lplStartThreads, // number of running threads
IN LPLONG lplStopThreads, // number of stopped threads
IN LPSECURITY_ATTRIBUTES lpThreadAttributes, // thread attributes
IN DWORD dwStackSize, // stack size
IN LPTHREAD_START_ROUTINE lpStartAddress, // start address
IN LPVOID lpParameter, // parameter
IN DWORD dwCreationFlags, // creation flags
OUT LPDWORD lpThreadId) // thread ID
{
HRESULT hr = NOERROR;
HGLOBAL hglobal = NULL;
HANDLE handle = NULL;
struct _tparam *lpContext = NULL;
DEBUGPUBLIC("HServiceCreateThread()\n");
SetLastError(ERROR_SUCCESS);
hr = CHK_HrEDKCreateThread(
lplStartThreads,
lplStopThreads,
lpThreadAttributes,
dwStackSize,
lpStartAddress,
lpParameter,
dwCreationFlags,
lpThreadId);
if(FAILED(hr))
return(NULL);
lpContext = (struct _tparam *)GlobalAlloc(GPTR, sizeof(struct _tparam));
if(lpContext == NULL)
{
hr = HR_LOG(E_FAIL);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
lpContext->lplStartThreads = lplStartThreads;
lpContext->lplStopThreads = lplStopThreads;
lpContext->lpStartAddress = lpStartAddress;
lpContext->lpParameter = lpParameter;
handle = CreateThread(
lpThreadAttributes,
dwStackSize,
(LPTHREAD_START_ROUTINE)FnCreateThread,
(LPVOID)lpContext,
dwCreationFlags,
lpThreadId);
cleanup:
if((handle == NULL) && (lpContext != NULL))
{
hr = HR_LOG(E_FAIL);
__try
{
hglobal = GlobalFree(lpContext);
if(hglobal != NULL)
{
hr = HR_LOG(E_FAIL);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(_exception_code());
hr = HR_LOG(E_FAIL);
}
}
return(handle);
}
//$--HrServiceGetName--------------------------------------------------------
// The HrServiceGetName function can be used by any thread to get the
// service name after the service has been started. lpszName must point to a
// block of memory at least MAX_SERVICE_NAME_LENGTH+1 bytes in length.
// -----------------------------------------------------------------------------
HRESULT HrServiceGetName( // RETURNS: return code
OUT LPSTR lpszName) // pointer to service name buffer
{
HRESULT hr = NOERROR;
DEBUGPUBLIC("HrServiceGetName()\n");
hr = CHK_HrServiceGetName(
lpszName);
if(FAILED(hr))
RETURN(hr);
lstrcpyn(lpszName, szServiceName, MAX_SERVICE_NAME_LENGTH+1);
lpszName[MAX_SERVICE_NAME_LENGTH] = 0;
RETURN(hr);
}
//$--HrServiceGetArgv--------------------------------------------------------
// The HrServiceGetArgv function can be used by any thread to get the
// service argv[] after the service has been started.
// -----------------------------------------------------------------------------
HRESULT HrServiceGetArgv( // RETURNS: return code
OUT DWORD *lpdwArgc, // pointer to argc address variable
OUT LPSTR **lppszArgv) // pointer to argv[] address variable
{
HRESULT hr = NOERROR;
DEBUGPUBLIC("HrServiceGetArgv()\n");
hr = CHK_HrServiceGetArgv(
lpdwArgc,
lppszArgv);
if(FAILED(hr))
RETURN(hr);
*lppszArgv = lpszServiceArgv;
*lpdwArgc = dwServiceArgc;
RETURN(hr);
}
//$--WaitShutdown---------------------------------------------------------------
// Thread that will send WM_DESTROY once hShutdownEvent is signalled.
//------------------------------------------------------------------------------
void WaitShutdown( // RETURNS: nothing
IN HANDLE hShutdownEvent) // Handle to Shutdown event object
{
HRESULT hr = NOERROR;
DWORD dw = 0;
DEBUGPUBLIC("WaitShutdown()\n");
hr = CHK_HrWaitShutdown(
hShutdownEvent);
if(FAILED(hr))
return;
if(hAppWnd == NULL)
{
hr = HR_LOG(E_FAIL);
return;
}
dw = WaitForSingleObject(hShutdownEvent, INFINITE);
if((dw != WAIT_OBJECT_0) && (dw != WAIT_ABANDONED))
{
hr = HR_LOG(E_FAIL);
}
if(hAppWnd != NULL)
{
PostMessage(hAppWnd, WM_DESTROY, 0, 0);
}
ExitThread(0);
return;
}
//$--fInitApplication-----------------------------------------------------------
// Initializes window data and registers window class
// -----------------------------------------------------------------------------
BOOL fInitApplication( // RETURNS: TRUE if successful
IN HINSTANCE hInstance) // Handle to instance of application
{
WNDCLASS wc = {0};
DEBUGPUBLIC("fInitApplication()\n");
// Fill in window class structure with parameters that describe the
// main window.
wc.style = CS_HREDRAW | CS_VREDRAW; // Class style(s).
wc.lpfnWndProc = (WNDPROC)WndProc; // Window Procedure
wc.cbClsExtra = 0; // No per-class extra data.
wc.cbWndExtra = 0; // No per-window extra data.
wc.hInstance = hInstance; // Owner of this class
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); // Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Cursor
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);// Default color
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Name to register as
if(wc.hIcon == NULL)
{
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Icon
}
// Register the window class and return success/failure code.
return(RegisterClass(&wc));
}
//$--fInitInstance--------------------------------------------------------------
// Saves instance handle and creates main window
// -----------------------------------------------------------------------------
BOOL fInitInstance( // RETURNS: TRUE if successful
IN HINSTANCE hInstance, // handle to instance of application
IN int nCmdShow) // argument for ShowWindow()
{
HRESULT hr = NOERROR;
DEBUGPUBLIC("fInitInstance()\n");
// Save the instance handle in static variable, which will be used in
// many subsequence calls from this application to Windows.
hInst = hInstance; // Store instance handle in our global variable
// Create a main window for this application instance.
hAppWnd = CreateWindow(
szAppName, // See RegisterClass() call.
szWindowTitle, // Text for window title bar.
WS_MINIMIZE |
WS_POPUP |
WS_SYSMENU |
WS_VISIBLE, // Window style.
CW_USEDEFAULT, 0, // Use default positioning
CW_USEDEFAULT, 0, // Use default positioning
NULL, // Overlapped windows have no parent.
NULL, // Use the window class menu.
hInstance, // This instance owns this window.
NULL // We don't use any data in our WM_CREATE
);
//
// If window could not be created, return "failure"
//
if(hAppWnd == NULL)
{
hr = HR_LOG(E_FAIL);
return(FALSE);
}
if(fIsService == FALSE)
{
//
// Make the window visible; update its client area; and return "success"
//
ShowWindow(hAppWnd, nCmdShow); // Show the window
UpdateWindow(hAppWnd); // Sends WM_PAINT message
}
return(TRUE);
}
//$--WorkerThread---------------------------------------------------------------
// This function does the actual nuts and bolts work that
// the service requires. It will also Pause or Stop when
// asked by the ServiceCtrlProc function.
// -----------------------------------------------------------------------------
VOID WorkerThread( // RETURNS: nothing
IN HANDLE hShutdownEvent) // service shutdown event handle
{
HRESULT hr = NOERROR;
HINSTANCE hInstance = NULL;
HINSTANCE hPrevInstance = NULL;
LPSTR lpszCmdLine = NULL;
int nCmdShow = 0;
MSG msg = {0};
BOOL fT = FALSE;
DEBUGPUBLIC("WorkerThread()\n");
hInstance = WinMainContext.hInstance;
hPrevInstance = WinMainContext.hPrevInstance;
lpszCmdLine = WinMainContext.lpszCmdLine;
nCmdShow = WinMainContext.nCmdShow;
hr = CHK_HrWorkerThread(
hShutdownEvent);
if(FAILED(hr))
return;
// Are there other instances of the app running?
if(hPrevInstance == NULL)
{// YES, so initialize shared things.
if(fInitApplication(hInstance) == FALSE)
{
hr = HR_LOG(E_FAIL);
ServiceStop();
goto cleanup;
}
}
// Perform initializations that apply to a specific instance
if(fInitInstance(hInstance, nCmdShow) == FALSE)
{
hr = HR_LOG(E_FAIL);
ServiceStop();
goto cleanup;
}
hr = HrServiceStartup(hInstance, hPrevInstance, hAppWnd, lpszCmdLine);
if(FAILED(hr))
{
ServiceStop();
goto cleanup;
}
//
// Create a thread that waits for the shut down event to trigger.
//
hShutdownThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)WaitShutdown,
(LPVOID)hShutdownEvent,
0,
&dwShutdownTID);
if(hShutdownThread == NULL)
{
hr = HR_LOG(E_FAIL);
ServiceStop();
//
// The service thread has not started yet so just shut down.
//
(void)HrServiceShutdown();
goto cleanup;
}
// Start a thread that executes the ServiceMain function.
hServiceThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)ServiceMain,
(LPVOID)hShutdownEvent,
0,
&dwServiceTID);
if(hServiceThread == NULL)
{
hr = HR_LOG(E_FAIL);
ServiceStop();
}
// Acquire and dispatch messages until a WM_QUIT message is received.
while (GetMessage(
&msg, // message structure
NULL, // handle of window receiving the message
0, // lowest message to examine
0)) // highest message to examine
{
TranslateMessage(&msg);// Translates virtual key codes
DispatchMessage(&msg); // Dispatches message to window
}
hr = NOERROR;
cleanup:
if(FAILED(hr))
SetServiceExitCode( ERROR_SERVICE_LOGON_FAILED, NOERROR);
CLOSEHANDLE(hShutdownThread);
ExitThread(0);
return;
}
//$--SzMakeServiceName----------------------------------------------------------
// Make a service name from the EXE name.
// -----------------------------------------------------------------------------
static LPSTR SzMakeServiceName( // RETURNS: name of service
IN LPSTR lpszExe) // executable name
{
LPSTR lpszCurr = NULL;
LPSTR lpszName = NULL;
if(lpszExe == NULL)
{
return(NULL);
}
lpszName = lpszExe + lstrlen(lpszExe);
while(lpszName != lpszExe)
{
//
// Check if character is a delimiter
//
lpszCurr = ":\\";
while(*lpszCurr)
{
if(*lpszName == *lpszCurr)
{
//
// Found a delimiter
//
goto cleanup;
}
lpszCurr++;
}
//
// Remove file extension
//
if(*lpszName == '.')
{
*lpszName = 0;
}
lpszName--;
}
cleanup:
if(lpszName != lpszExe)
{
//
// Advance past delimiter
//
lpszName++;
}
return(lpszName);
}
//$--dwDoServiceCtrlPause-------------------------------------------------------
// This function is called by ServiceCtrl whenever
// someone calls ControlService to pause our service.
// -----------------------------------------------------------------------------
static DWORD dwDoServiceCtrlPause( // RETURNS: service state
VOID) // no arguments
{
HRESULT hr = NOERROR;
DWORD dwState = SERVICE_RUNNING;
DWORD dwStatus = 0;
BOOL fStatus = TRUE;
DEBUGPUBLIC("dwDoServiceCtrlPause()\n");
dwState = ssStatus.dwCurrentState;
if(ssStatus.dwCurrentState == SERVICE_RUNNING)
{
// Report the status, specifying the checkpoint and wait-hint,
// before setting the continue event.
dwState = SERVICE_PAUSED;
dwPauseCheckPoint++;
FServiceReportStatus(
dwState, // current state
NOERROR, // exit code
NOERROR, // exit code
dwPauseCheckPoint, // checkpoint
dwPauseWaitHint); // wait hint
if((hServicePauseEvent != NULL) && (hServiceContinueEvent != NULL))
{
fStatus = ResetEvent(hServiceContinueEvent);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
fStatus = SetEvent(hServicePauseEvent);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
}
else
{
hr = HR_LOG(E_FAIL);
}
}
return(dwState);
}
//$--dwDoServiceCtrlContinue----------------------------------------------------
// This function is called by ServiceCtrl whenever
// someone calls ControlService to continue our service.
// -----------------------------------------------------------------------------
static DWORD dwDoServiceCtrlContinue( // RETURNS: service state
VOID) // no arguments
{
HRESULT hr = NOERROR;
DWORD dwState = SERVICE_RUNNING;
DWORD dwStatus = 0;
BOOL fStatus = TRUE;
DEBUGPUBLIC("dwDoServiceCtrlContinue()\n");
dwState = ssStatus.dwCurrentState;
if(ssStatus.dwCurrentState == SERVICE_PAUSED)
{
// Report the status, specifying the checkpoint and wait-hint,
// before setting the continue event.
dwState = SERVICE_RUNNING;
dwContinueCheckPoint++;
FServiceReportStatus(
dwState, // current state
NOERROR, // exit code
NOERROR, // exit code
dwContinueCheckPoint, // checkpoint
dwContinueWaitHint); // wait hint
if((hServicePauseEvent != NULL) && (hServiceContinueEvent != NULL))
{
fStatus = ResetEvent(hServicePauseEvent);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
fStatus = SetEvent(hServiceContinueEvent);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
}
else
{
hr = HR_LOG(E_FAIL);
}
}
return(dwState);
}
//$--dwDoServiceCtrlStop--------------------------------------------------------
// This function is called by ServiceCtrl whenever
// someone calls ControlService to stop our service.
// -----------------------------------------------------------------------------
static DWORD dwDoServiceCtrlStop( // RETURNS: service state
VOID) // no arguments
{
HRESULT hr = NOERROR;
DWORD dwState = SERVICE_RUNNING;
DWORD dwStatus = 0;
BOOL fStatus = TRUE;
DEBUGPUBLIC("dwDoServiceCtrlStop()\n");
dwState = SERVICE_STOP_PENDING;
// Report the status, specifying the checkpoint and wait-hint,
// before setting the termination event.
dwStopCheckPoint++;
FServiceReportStatus(
SERVICE_STOP_PENDING, // current state
NOERROR, // exit code
NOERROR, // exit code
dwStopCheckPoint, // checkpoint
dwStopWaitHint); // wait hint
if(hServiceStopEvent != NULL)
{
fStatus = SetEvent(hServiceStopEvent);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
}
return(dwState);
}
//$--ServiceCtrlProc------------------------------------------------------------
// This function is called by the Service Controller whenever
// someone calls ControlService in reference to our service.
// -----------------------------------------------------------------------------
static VOID WINAPI ServiceCtrlProc( // RETURNS: nothing
IN DWORD dwCtrlCode) // control code
{
HRESULT hr = NOERROR;
DWORD dwState = SERVICE_RUNNING;
DWORD dwStatus = 0;
BOOL fStatus = FALSE;
DEBUGPUBLIC("ServiceCtrlProc()\n");
//
// Handle the requested control code.
//
switch(dwCtrlCode)
{
// Pause the service if it is running.
case SERVICE_CONTROL_PAUSE:
dwState = dwDoServiceCtrlPause();
break;
// Resume the paused service.
case SERVICE_CONTROL_CONTINUE:
dwState = dwDoServiceCtrlContinue();
break;
// Stop the service.
case SERVICE_CONTROL_STOP:
dwState = dwDoServiceCtrlStop();
return;
// Update the service status.
case SERVICE_CONTROL_INTERROGATE:
break;
// Invalid control code
default:
break;
}
//
// Send a status response.
//
fStatus = FServiceReportStatus(dwState, NOERROR, NOERROR, 0, 0);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
}
//$--_ServiceMain---------------------------------------------------------------
// This function takes care of actually starting the service,
// informing the service controller at each step along the way.
// After launching the worker thread, it waits on the event
// that the worker thread will signal at its termination.
// -----------------------------------------------------------------------------
VOID _ServiceMain( // RETURNS: nothing
IN DWORD dwArgc, // count of arguments
IN LPSTR *lpszArgv) // pointer to arguments
{
HRESULT hr = NOERROR;
DWORD dw = 0;
DWORD dwThreadID = 0;
BOOL fStatus = FALSE;
DEBUGPUBLIC("_ServiceMain()");
hr = CHK__ServiceMain(
dwArgc,
lpszArgv);
if(FAILED(hr))
return;
// Register our service control handler:
lstrcpyn(
szServiceName,
SzMakeServiceName(lpszArgv[0]),
MAX_SERVICE_NAME_LENGTH);
szServiceName[MAX_SERVICE_NAME_LENGTH] = 0;
dwServiceArgc = dwArgc;
lpszServiceArgv = lpszArgv;
if(fIsService == TRUE)
{
sshStatusHandle = RegisterServiceCtrlHandler(
szServiceName,
ServiceCtrlProc);
if(sshStatusHandle == 0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ssStatus.dwServiceSpecificExitCode = 0;
// Report the status to Service Control Manager.
dwStartCheckPoint++;
if(FServiceReportStatus(
SERVICE_START_PENDING, // service state
NOERROR, // exit code
NOERROR, // exit code
dwStartCheckPoint, // checkpoint
dwStartWaitHint) == FALSE) // wait hint
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Create the event object. The control handler function signals
// this event when it receives the "stop" control code.
hServiceStopEvent = CreateEvent(
NULL, // no security attributes
TRUE, // manual reset event
FALSE, // not-signalled
NULL); // no name
if(hServiceStopEvent == (HANDLE)NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Create the event object. The control handler function signals
// this event when it receives the "pause" control code.
hServicePauseEvent = CreateEvent(
NULL, // no security attributes
TRUE, // manual reset event
FALSE, // not-signalled
NULL); // no name
if(hServicePauseEvent == (HANDLE)NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Create the event object. The control handler function signals
// this event when it receives the "continue" control code.
hServiceContinueEvent = CreateEvent(
NULL, // no security attributes
TRUE, // manual reset event
FALSE, // not-signalled
NULL); // no name
if(hServiceContinueEvent == (HANDLE)NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Create the event object. The application signals
// this event when it is ready to "stop".
hStopConfirmEvent = CreateEvent(
NULL, // no security attributes
TRUE, // manual reset event
FALSE, // not-signalled
NULL); // no name
if(hStopConfirmEvent == (HANDLE)NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Report the status to the service control manager.
dwStartCheckPoint++;
if(FServiceReportStatus(
SERVICE_START_PENDING, // service state
NOERROR, // exit code
NOERROR, // exit code
dwStartCheckPoint, // checkpoint
dwStartWaitHint) == FALSE) // wait hint
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Start the thread that performs the work of the service.
hWorkerThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)WorkerThread,
(LPVOID)hServiceStopEvent,
0,
&dwWorkerTID);
if(hWorkerThread == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Report the status to the service control manager.
if(FServiceReportStatus(
SERVICE_RUNNING, // service state
NOERROR, // exit code
NOERROR, // exit code
0, // checkpoint
0) == FALSE) // wait hint
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
//
// Wait indefinitely until worker thread exits.
//
dw = WaitForSingleObject(hWorkerThread, INFINITE);
if((dw != WAIT_OBJECT_0) && (dw != WAIT_ABANDONED))
{
hr = HR_LOG(E_FAIL);
}
cleanup:
CLOSEHANDLE(hWorkerThread);
CLOSEHANDLE(hServiceStopEvent);
CLOSEHANDLE(hServicePauseEvent);
CLOSEHANDLE(hServiceContinueEvent);
CLOSEHANDLE(hStopConfirmEvent);
//
// Try to report the stopped status to the service control manager.
//
if(sshStatusHandle != 0)
{
fStatus = FServiceReportStatus(
SERVICE_STOPPED,
dwSvcWin32ExitCode,
dwSvcServiceSpecificExitCode,
0,
0);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
}
// When SERVICE MAIN FUNCTION returns in a single service
// process, the StartServiceCtrlDispatcher function in
// the main thread returns, terminating the process.
return;
}
//$--HrWaitServiceStopConfirm---------------------------------------------------
// This function is called to wait for the application to stop.
// -----------------------------------------------------------------------------
HRESULT HrWaitServiceStopConfirm( // RETURNS: return code
VOID) // no arguments
{
HRESULT hr = NOERROR;
DWORD dwT = 0;
DWORD dwStopTimeOut = dwStopWaitHint/2;
DEBUGPUBLIC("HrWaitServiceStopConfirm()\n");
for(;;)
{
if(fIsService == TRUE)
{
// Report the status, specifying the checkpoint and wait-hint.
dwStopCheckPoint++;
FServiceReportStatus(
SERVICE_STOP_PENDING, // current state
NOERROR, // exit code
NOERROR, // exit code
dwStopCheckPoint, // checkpoint
dwStopWaitHint); // wait hint
}
//
// Wait until application has stopped or a timeout occurs.
//
dwT = WaitForSingleObject(hStopConfirmEvent, dwStopTimeOut);
switch(dwT)
{
case (WAIT_OBJECT_0):
case (WAIT_ABANDONED_0):
hr = NOERROR;
goto cleanup;
break;
case WAIT_TIMEOUT:
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
cleanup:
RETURN(hr);
}
//$--SetServiceExitCode---------------------------------------------------------
// This function is called by the application to set the service exit code.
//
// dwWin32ExitCode
//
// - specifies a Win32 error code that the service uses to
// report an error that occurs when it is starting or
// stopping. To return an error code specific to the service,
// the service must set this value to
// ERROR_SERVICE_SPECIFIC_ERROR to indicate that the
// dwServiceSpecificExitCode member contains the error code.
// The service should set this value to NO_ERROR when it is
// running and on normal termination.
//
// dwServiceSpecificExitCode
//
// - specifies a service specific error code that the
// service returns when an error occurs while the service is
// starting or stopping. This value is ignored unless the
// dwWin32ExitCode member is set to ERROR_SERVICE_SPECIFIC_ERROR.
//
// -----------------------------------------------------------------------------
VOID SetServiceExitCode(
IN DWORD dwWin32ExitCode, // Win32 exit code
IN DWORD dwServiceSpecificExitCode) // service specific exit code
{
DEBUGPUBLIC("SetServiceExitCode()");
dwSvcWin32ExitCode = dwWin32ExitCode;
dwSvcServiceSpecificExitCode = dwServiceSpecificExitCode;
}
//$--HrServiceConfirmStop----------------------------------------------------
// This function is called by the application to indicate that it has stopped.
// -----------------------------------------------------------------------------
HRESULT HrServiceConfirmStop( // RETURNS: return code
VOID) // no arguments
{
HRESULT hr = NOERROR;
BOOL fStatus = TRUE;
DEBUGPUBLIC("HrServiceConfirmStop()\n");
if(hStopConfirmEvent != NULL)
{
fStatus = SetEvent(hStopConfirmEvent);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
}
else
{
hr = HR_LOG(E_FAIL);
}
RETURN(hr);
}
//------------------------------------------------------------------------------
// Utility functions...
//------------------------------------------------------------------------------
//$--FServiceReportStatus-------------------------------------------------------
// This function is called by the private _ServiceMain() and
// ServCtrlHandler() functions to update the service's status
// to the service control manager.
// -----------------------------------------------------------------------------
BOOL FServiceReportStatus( // RETURNS: TRUE if successful
IN DWORD dwCurrentState, // current state of service
IN DWORD dwWin32ExitCode, // service Win32 exit code
IN DWORD dwServiceSpecificExitCode, // service specific exit code
IN DWORD dwCheckPoint, // check point number
IN DWORD dwWaitHint) // time to wait
{
BOOL fResult = TRUE;
DEBUGPUBLIC("FServiceReportStatus()\n");
// Disable control requests until the service is started.
if(dwCurrentState == SERVICE_START_PENDING)
{
ssStatus.dwControlsAccepted = 0;
}
else
{
ssStatus.dwControlsAccepted =
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE;
}
// These SERVICE_STATUS members are set from parameters.
ssStatus.dwCurrentState = dwCurrentState;
ssStatus.dwWin32ExitCode = dwWin32ExitCode;
ssStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
ssStatus.dwCheckPoint = dwCheckPoint;
ssStatus.dwWaitHint = dwWaitHint;
// Report the status of the service to the service control manager.
if(fIsService == TRUE)
{
fResult = SetServiceStatus(
sshStatusHandle, // service reference handle
&ssStatus); // SERVICE_STATUS structure
if(fResult == FALSE)
{
// If an error occurs, stop the service.
ServiceStop();
}
}
return(fResult);
}
//$--ServiceStop-------------------------------------------------------------
// This function can be used by any thread to stop the service.
// -----------------------------------------------------------------------------
VOID ServiceStop( // RETURNS: nothing
VOID) // no argument
{
HRESULT hr = NOERROR;
BOOL fStatus = TRUE;
DEBUGPUBLIC("ServiceStop()\n");
// Set a termination event to stop SERVICE MAIN FUNCTION.
if(hServiceStopEvent != NULL)
{
fStatus = SetEvent(hServiceStopEvent);
if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
}
}
//$--HrServiceWaitForControl----------------------------------------------------
// The HrServiceWaitForControl function can be used by any thread to get or
// wait for service control after the service has been started.
// -----------------------------------------------------------------------------
HRESULT HrServiceWaitForControl( // RETURNS: return code
IN DWORD dwTimeout, // time-out interval in milliseconds
OUT EDK_SERVICE_CONTROL_T *pedksc) // pointer to service name buffer
{
HRESULT hr = NOERROR;
DWORD cObjects = 3;
HANDLE hObjects[3] = {0};
DWORD dwResult = WAIT_FAILED;
DEBUGPUBLIC("HrServiceWaitForControl()\n");
hr = CHK_HrServiceWaitForControl(
dwTimeout,
pedksc);
if(FAILED(hr))
RETURN(hr);
hObjects[0] = hServiceStopEvent;
hObjects[1] = hServicePauseEvent;
hObjects[2] = hServiceContinueEvent;
dwResult = WaitForMultipleObjects(cObjects, hObjects, FALSE, dwTimeout);
*pedksc = EDK_SC_NONE;
switch(dwResult)
{
case (WAIT_OBJECT_0):
*pedksc = EDK_SC_STOP;
break;
case (WAIT_OBJECT_0+1):
*pedksc = EDK_SC_PAUSE;
break;
case (WAIT_OBJECT_0+2):
*pedksc = EDK_SC_CONTINUE;
break;
case (WAIT_ABANDONED_0):
break;
case (WAIT_ABANDONED_0+1):
break;
case (WAIT_ABANDONED_0+2):
break;
case WAIT_TIMEOUT:
break;
default:
*pedksc = EDK_SC_STOP;
hr = HR_LOG(E_FAIL);
}
RETURN(hr);
}
//$--HrServiceWaitForContinue---------------------------------------------------
// The HrServiceWaitForContinue function can be used by any thread to get or
// wait for service control after the service has been started.
// -----------------------------------------------------------------------------
HRESULT HrServiceWaitForContinue( // RETURNS: return code
IN DWORD dwTimeout, // time-out interval in milliseconds
OUT EDK_SERVICE_CONTROL_T *pedksc) // pointer to service name buffer
{
HRESULT hr = NOERROR;
DWORD cObjects = 2;
HANDLE hObjects[2] = {0};
DWORD dwResult = WAIT_FAILED;
DEBUGPUBLIC("HrServiceWaitForContinue()\n");
hr = CHK_HrServiceWaitForContinue(
dwTimeout,
pedksc);
if(FAILED(hr))
RETURN(hr);
hObjects[0] = hServiceStopEvent;
hObjects[1] = hServiceContinueEvent;
dwResult = WaitForMultipleObjects(cObjects, hObjects, FALSE, dwTimeout);
*pedksc = EDK_SC_NONE;
switch(dwResult)
{
case (WAIT_OBJECT_0):
*pedksc = EDK_SC_STOP;
break;
case (WAIT_OBJECT_0+1):
*pedksc = EDK_SC_CONTINUE;
break;
case (WAIT_ABANDONED_0):
break;
case (WAIT_ABANDONED_0+1):
break;
case WAIT_TIMEOUT:
break;
default:
*pedksc = EDK_SC_STOP;
hr = HR_LOG(E_FAIL);
}
RETURN(hr);
}
//$--HrServiceWaitForStop-------------------------------------------------------
// The HrServiceWaitForStop function can be used by any thread to get or
// wait for service control after the service has been started.
// -----------------------------------------------------------------------------
HRESULT HrServiceWaitForStop( // RETURNS: return code
IN DWORD dwTimeout, // time-out interval in milliseconds
OUT EDK_SERVICE_CONTROL_T *pedksc) // pointer to service name buffer
{
HRESULT hr = NOERROR;
DWORD cObjects = 1;
HANDLE hObjects[1] = {0};
DWORD dwResult = WAIT_FAILED;
DEBUGPUBLIC("HrServiceWaitForStop()\n");
hr = CHK_HrServiceWaitForStop(
dwTimeout,
pedksc);
if(FAILED(hr))
RETURN(hr);
hObjects[0] = hServiceStopEvent;
dwResult = WaitForMultipleObjects(cObjects, hObjects, FALSE, dwTimeout);
*pedksc = EDK_SC_NONE;
switch(dwResult)
{
case (WAIT_OBJECT_0):
*pedksc = EDK_SC_STOP;
break;
case (WAIT_ABANDONED_0):
break;
case WAIT_TIMEOUT:
break;
default:
*pedksc = EDK_SC_STOP;
hr = HR_LOG(E_FAIL);
}
RETURN(hr);
}
//$--HrServiceProcessControl---------------------------------------------------
// Check for service control
// -----------------------------------------------------------------------------
HRESULT HrServiceProcessControl( // RETURNS: return code
VOID) // no arguments
{
HRESULT hr = NOERROR;
HRESULT hrT = NOERROR;
EDK_SERVICE_CONTROL_T edksc = EDK_SC_NONE;
DEBUGPUBLIC("HrServiceProcessControl()\n");
for(;;)
{
switch(edksc)
{
case EDK_SC_PAUSE:
// Wait for service to continue or stop
hrT = HrServiceWaitForContinue(INFINITE, &edksc);
break;
default:
// Wait for service to pause, continue or stop
hrT = HrServiceWaitForControl(0, &edksc);
}
if(SUCCEEDED(hrT))
{
switch(edksc)
{
case EDK_SC_NONE:
goto cleanup;
break;
case EDK_SC_PAUSE:
break;
case EDK_SC_CONTINUE:
goto cleanup;
break;
case EDK_SC_STOP:
hr = HR_LOG(EDK_E_SHUTDOWN_SERVICE);
goto cleanup;
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
else
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
cleanup:
RETURN(hr);
}
//$--HrFreeArgvW----------------------------------------------------------------
// Free a wide character copy of argv[].
// -----------------------------------------------------------------------------
static HRESULT HrFreeArgvW( // RETURNS: exit code
IN DWORD dwArgc, // Number of arguments
IN OUT LPWSTR **lppszArgvW) // pointer to wide character command line
{
HRESULT hr = NOERROR;
DWORD i = 0;
LPWSTR *lpwsz = NULL;
DEBUGPRIVATE("HrFreeArgvW()\n");
hr = CHK_HrFreeArgvW(
dwArgc,
lppszArgvW);
if(FAILED(hr))
RETURN(hr);
lpwsz = *lppszArgvW;
if(lpwsz != NULL)
{
for(i = 0 ; i < dwArgc ; i++)
{
if(lpwsz[i] != NULL)
{
free(lpwsz[i]);
lpwsz[i] = NULL;
}
}
free(lpwsz);
lpwsz = NULL;
}
*lppszArgvW = NULL;
RETURN(hr);
}
//$--ControlHandler-------------------------------------------------------------
// Handle console control events.
// -----------------------------------------------------------------------------
static BOOL WINAPI ControlHandler( // RETURNS: TRUE if successful
IN DWORD dwCtrlType) // control event type
{
switch( dwCtrlType )
{
case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
ServiceStop();
return(TRUE);
break;
default:
;
}
return(FALSE);
}
//$--WinMain--------------------------------------------------------------------
// This function starts the service.
// -----------------------------------------------------------------------------
int APIENTRY WinMain( // RETURNS: exit code
IN HINSTANCE hInstance, // Handle to instance of application
IN HINSTANCE hPrevInstance, // Handle to previous instance
IN LPSTR lpszCmdLine, // Pointer to command line
IN int nCmdShow) // Argument for ShowWindow()
{
HRESULT hr = NOERROR;
DWORD dwArgc = 0;
LPSTR* lpszArgv = NULL;
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{ SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)_ServiceMain },
{ NULL, NULL }
};
DEBUGPUBLIC("WinMain()\n");
//
// Parse command line
//
dwArgc = (DWORD) __argc;
lpszArgv = __argv;
//
// Check if running as a service
//
if((dwArgc > 1) && (lstrcmpi(lpszArgv[1], "NOTSERV") == 0))
{
fIsService = FALSE;
}
WinMainContext.hInstance = hInstance;
WinMainContext.hPrevInstance = hPrevInstance;
WinMainContext.lpszCmdLine = lpszCmdLine;
WinMainContext.nCmdShow = nCmdShow;
if(fIsService == TRUE)
{
//
// Run as a service
//
if(StartServiceCtrlDispatcher(dispatchTable) == FALSE)
{
hr = HR_LOG(E_FAIL);
ServiceStop();
}
}
else
{
SetConsoleCtrlHandler(ControlHandler, TRUE);
if(dwArgc == 2)
{
hr = HR_LOG(E_FAIL);
}
else
{
_ServiceMain(dwArgc-2, lpszArgv+2); // NOTE: if we're running by command line,
// need to pass arg after 'NOTSERV' as service
// name rather than exe name.
}
}
return(0);
}
//$--WndProc--------------------------------------------------------------------
// Processes messages
// -----------------------------------------------------------------------------
LRESULT CALLBACK WndProc( // RETURNS: LRESULT
IN HWND hWnd, // window handle
IN UINT message, // type of message
IN WPARAM uParam, // additional information
IN LPARAM lParam) // additional information
{
HRESULT hr = NOERROR;
HRESULT hrT = NOERROR;
LRESULT lRetVal = 0;
BOOL fStatus = TRUE;
DWORD dw = 0;
DEBUGPUBLIC("WndProc()\n");
switch( message)
{
// Closing the window will signal the shutdown event object and cause
// WaitShutdown to send a WM_DESTROY message and end its thread.
case WM_CLOSE:
ServiceStop();
break;
// Before destroying the window, call NTServiceShutdown.
case WM_DESTROY:
//
// Wait for application to stop.
//
hrT = HR_LOG(HrWaitServiceStopConfirm());
//
// Wait for service thread to exit.
//
if(hServiceThread != NULL)
{
dw = WaitForSingleObject(hServiceThread, WAIT_DESTROY);
if((dw != WAIT_OBJECT_0) && (dw != WAIT_ABANDONED_0))
{
hr = HR_LOG(E_FAIL);
}
CLOSEHANDLE(hServiceThread);
}
hrT = HR_LOG(HrServiceShutdown());
//
// Cause message loop in WorkerThread() to terminate.
//
PostQuitMessage(0);
break;
// Paint the icon if its background is erased.
case WM_ICONERASEBKGND:
DefWindowProc(hWnd, WM_ICONERASEBKGND, uParam, lParam);
PostMessage(hWnd, WM_PAINTICON, 0, 0);
break;
// Users are not allowed to "Switch To" this application via Task Manager
// since it has no main window. According to the API, if the icon can not
// be opened, this message should return FALSE.
case WM_QUERYOPEN:
lRetVal = (LRESULT)FALSE;
break;
// Do the default action for any other message.
default:
lRetVal = DefWindowProc(hWnd, message, uParam, lParam);
break;
}
return(lRetVal);
}