Linking the DLL File
Next, link the resource file to generate the DLL.
After completing the previous tasks, the following screenshot shows the generated files seen through Windows explorer.
Adding the C++ Source File
Next, add the source file for our first project and give it a suitable name.
Add the following source code.
// link with Kernel32.lib and Advapi32.lib
#include <windows.h>
#include <strsafe.h>
// The file winservice.h is generated when building the resource-only DLL, winservice.dll
#include ../winservice.h
// #pragma comment(lib, advapi32.lib)
// Change the service name accordingly
#define SVCNAME LMyWinServiceName
SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
VOID SvcInstall(void);
VOID WINAPI SvcCtrlHandler( DWORD );
VOID WINAPI SvcMain( DWORD, LPTSTR * );
VOID ReportSvcStatus( DWORD, DWORD, DWORD );
VOID SvcInit( DWORD, LPTSTR * );
VOID SvcReportEvent( LPTSTR );
// Purpose: Entry point for the process
// Parameters: None
// Return value: None
void wmain(int argc, WCHAR *argv[])
{
// If command-line parameter is install, install the service.
// Otherwise, the service is probably being started by the SCM
if(argc != 2)
{
wprintf(LUsage: %s install\n,argv[0]);
wprintf(L Otherwise, service probably being started by SCM...\n);
}
if( lstrcmpi( argv[1], Linstall) == 0 )
{
wprintf(LInstalling the service....\n);
SvcInstall();
return;
}
// TO_DO: Add any additional services for the process to this table.
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
{ NULL, NULL }
};
// This call returns when the service has stopped.
// The process should simply terminate when the call returns
if (!StartServiceCtrlDispatcher(DispatchTable))
{
SvcReportEvent(LStartServiceCtrlDispatcher()...);
}
}
// Purpose: Installs a service in the SCM database
// Parameters: None
// Return value: None
VOID SvcInstall()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
WCHAR szPath[MAX_PATH];
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
{
wprintf(LCannot install %s service, error %d\n, SVCNAME, GetLastError());
return;
}
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (schSCManager == NULL)
{
wprintf(LOpenSCManager() failed, error %d\n, GetLastError());
return;
}
else
wprintf(LOpenSCManager() is OK!\n);
// Create the service
schService = CreateService(
schSCManager, // SCM database
SVCNAME, // name of service
SVCNAME, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL)
{
wprintf(LCreateService() failed error %d\n, GetLastError());
CloseServiceHandle(schSCManager);
return;
}
else
wprintf(LService %s installed successfully\n, SVCNAME);
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
// Purpose: Entry point for the service
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of
// the service and subsequent strings are passed by the process
// that called the StartService function to start the service.
// Return value: None.
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
{
// Register the handler function for the service
gSvcStatusHandle = RegisterServiceCtrlHandler(SVCNAME,SvcCtrlHandler);
if(!gSvcStatusHandle )
{
SvcReportEvent(LRegisterServiceCtrlHandler());
return;
}
// These SERVICE_STATUS members remain as set here
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
gSvcStatus.dwServiceSpecificExitCode = 0;
// Report initial status to the SCM
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );
// Perform service-specific initialization and work
SvcInit( dwArgc, lpszArgv );
}
// Purpose: The service code
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of
// the service and subsequent strings are passed by the process
// that called the StartService function to start the service.
// Return value: None
//
VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)
{
// TO_DO: Declare and set any required variables.
// Be sure to periodically call ReportSvcStatus() with
// SERVICE_START_PENDING. If initialization fails, call
// ReportSvcStatus with SERVICE_STOPPED.
// Create an event. The control handler function, SvcCtrlHandler(),
// signals this event when it receives the stop control code
ghSvcStopEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
NULL); // no name
if ( ghSvcStopEvent == NULL)
{
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
// Report running status when initialization is complete.
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );
// TO_DO: Perform work until service stops.
while(1)
{
// Check whether to stop the service.
WaitForSingleObject(ghSvcStopEvent, INFINITE);
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
}
// Purpose: Sets the current service status and reports it to the SCM.
// Parameters:
// dwCurrentState - The current state (see SERVICE_STATUS)
// dwWin32ExitCode - The system error code
// dwWaitHint - Estimated time for pending operation, in milliseconds
// Return value: None
VOID ReportSvcStatus( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
// Fill in the SERVICE_STATUS structure
gSvcStatus.dwCurrentState = dwCurrentState;
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
gSvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING)
{
wprintf(LThe %s service is starting...\n, SVCNAME);
gSvcStatus.dwControlsAccepted = 0;
}
else
{
wprintf(LThe %s service can be stopped...\n, SVCNAME);
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
}
if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) )
gSvcStatus.dwCheckPoint = 0;
else
gSvcStatus.dwCheckPoint = dwCheckPoint++;
// Report the status of the service to the SCM.
SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
}
// Purpose:
// Called by SCM whenever a control code is sent to the service
// using the ControlService function.
// Parameters: dwCtrl - control code
// Return value: None
VOID WINAPI SvcCtrlHandler(DWORD dwCtrl)
{
// Handle the requested control code.
switch(dwCtrl)
{
case SERVICE_CONTROL_STOP:
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
// Signal the service to stop.
SetEvent(ghSvcStopEvent);
return;
case SERVICE_CONTROL_INTERROGATE:
// Fall through to send current status.
break;
default:
break;
}
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
}
// Purpose: Logs messages to the event log
// Parameters: szFunction - name of function that failed
// Return value: None
// Remarks: The service must have an entry in the Application event log.
VOID SvcReportEvent(LPTSTR szFunction)
{
HANDLE hEventSource;
LPCTSTR lpszStrings[2];
WCHAR Buffer[80];
hEventSource = RegisterEventSource(NULL, SVCNAME);
if( hEventSource != NULL )
{
StringCchPrintf(Buffer, 80, L%s failed with %d, szFunction, GetLastError());
lpszStrings[0] = SVCNAME;
lpszStrings[1] = Buffer;
ReportEvent(hEventSource, // event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // event category
SVC_ERROR, // event identifier
NULL, // no security identifier
2, // size of lpszStrings array
0, // no binary data
lpszStrings, // array of strings
NULL); // no binary data
DeregisterEventSource(hEventSource);
}
}