NT Service and Inter-process Communication using Mailslot






4.40/5 (7 votes)
This article explains about different parts of an NT service and inter-process communication using Mailslots.
Introduction
This article explains about different parts of an NT service and inter-process communication using Mailslots.
Different Aspects of an NT Service
Major aspects that need to be covered when writing an NT service are detailed out below. Following are the major aspects that are covered in this article:
- Creating/Registering a service
- Removing a service
- Registering
ServiceMain()
ServiceHandler()
- Setting Service status
1) Creating/Registering a Service
Service can be created by opening the SCM. This is generally done in the WinMain()
or main()
function based on a command line parameter that is passed. OpenSCManager()
will give the SCM handle. With this handle, CreateService()
will have to be called to get the service created.
Once the service EXE is ready, to create the service, go to command prompt and move to the directory where the service binary is present and type the following: SigmaSrv /register. Calls within the binary to create the service are shown below:
SC_HANDLE schSCManager = NULL; // Service Control Manager handle
SC_HANDLE schService = NULL; // Service Handle
if(0 == _tcsicmp((const _TCHAR *)lpCmdLine, (const _TCHAR *)"/register"))
{
// Open SCM and register service
schSCManager = OpenSCManager(NULL, // local machine
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
// Get the executable file path
TCHAR szFilePath[_MAX_PATH];
::GetModuleFileName(NULL, szFilePath, _MAX_PATH);
schService = CreateService(schSCManager, // Handle to SCM
TEXT(SIGMA_SUPPORT_SERVICE_NAME), // name of service
TEXT("Sigma Support Service"), // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // own process
SERVICE_DEMAND_START, // SigmaMain will start this service
SERVICE_ERROR_NORMAL, // error control type
szFilePath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL);
if(NULL == schService)
{
MessageBox(NULL, TEXT(lpCmdLine), TEXT("Sigma"), MB_OK);
}
CloseServiceHandle(schSCManager);
2) Removing a Service
Similar to creating a service, removing a service is also initiated based on the command line argument. In this case also SCM will have to be opened using OpenSCManager()
, followed by opening the service with all rights using OpenService()
. Then DeleveService()
will have to be called to delete the service.
To remove the service, go to command prompt and move to the directory where the service binary is present and type the following: SigmaSrv /unregister. Calls within the binary to get this done are shown below:
else if(0 == _tcsicmp((const _TCHAR *)lpCmdLine, (const _TCHAR *)"/unregister"))
{
schSCManager = NULL;
schService = NULL; // Service Handle
// Open SCM and register service
schSCManager = OpenSCManager(NULL, // local machine
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
schService = OpenService(schSCManager, // SCManager database
TEXT("SigmaSrv"), // name of service
SC_MANAGER_ALL_ACCESS); // only need DELETE access
if (schService == NULL)
{
//Log this state
}
if (!DeleteService(schService) )
{
//Log if not able to delete the service
}
CloseServiceHandle(schService);
3) Registering ServiceMain()
ServiceMain()
is a callback function and the starting point for a service. Reference to ServiceMain()
is given via a call to StartServiceCtrlDispatcher()
in WinMain()
. SERVICE_TABLE_ENTRY
structure is used to pass the service name and its associated starting point (ServiceMain()
in this case) through the StartServiceCtrlDispatcher()
call.
There could be multiple services housed in the same service via this structure. {NULL, NULL}
is required to mark the end of this array. ServiceMain()
function can have any name you wish, it need not be ServiceMain()
always. Calls to register ServiceMain()
are shown below. This is done in WinMain()
:
SERVICE_TABLE_ENTRY srvDispatchTable[]=
{{ TEXT(SIGMA_SUPPORT_SERVICE_NAME), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
{ NULL, NULL }};
DWORD dw_Error = 0;
g_hServiceStatusThread = CreateThread(0,
0,
(LPTHREAD_START_ROUTINE) SigmaSupportServiceStatusThread,
0,
NULL,
&dw_Error);
//Start service
StartServiceCtrlDispatcher(srvDispatchTable);
4) SeviceHandler()
ServiceHandler
is another callback function which is registered in ServiceMain()
by calling RegisterServiceCtrlHandler()
. This is done within the ServiceMain()
function. This function calls the user defined service functions based on the control requests received (Example: control request to stop the service, pause the service, etc.). Arguments for RegisterServiceCtrlHandler()
are the service name and the ServiceHandler()
proc name. Here also ServiceHandler()
can be given any name. Call to register the ServiceHandler()
is shown below:
g_hServiceStatusHandle = RegisterServiceCtrlHandler(TEXT(SIGMA_SUPPORT_SERVICE_NAME)
, ServiceHandler);
5) Setting Service Status
SetServiceStatus()
should be called to set the status of the service. This is required for the SCM to correctly understand the status of the service. In this article's source, SetServiceStatus()
is encased in the function SetSigmaServiceStatus()
.
Mailslot
To show that the service is running, mailslots had been made use of in this sample. Mailslot is an interprocess communication mechanism. Service will periodically update its status into the status bar of a client application. This client application is available on CodeProject at the following location: Sigma.aspx. (Please bear with me till the client application is updated.)
Client will have the mailslot open and this service will keep updating its status into the mailslot. It is up to the client to read this on a periodic basis and display/convey the information to the user.
Service opens the mailslot using CreateFile()
and keeps writing its status message into mailslot using WriteFile()
. In the sample, WriteFile()
is encased in the function PostToMailSlot()
. Following is the code snippet that the service uses:
LPTSTR lpszSlotName = TEXT("\\\\.\\mailslot\\sigmamain");
.
.
g_hFile = CreateFile(lpszSlotName,
GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
.
.
bResult = WriteFile(g_hFile,
lpszMessage,
(DWORD) lstrlen(lpszMessage) + 1, // add null termination
&dw_MsgWrittenLen,
(LPOVERLAPPED) NULL);
Steps to get the message on the client side are: Create the mailslot, Get mailslot information and Read mailslot messages. The calls on the client side to get the mailslot messages are shown below:
#define MAILSLOTNAME \\\\.\\mailslot\\sigmamain
.
.
ghSlot = CreateMailslot(TEXT(MAILSLOTNAME),
0, // no maximum message size
MAILSLOT_WAIT_FOREVER, // no time-out for operations
(LPSECURITY_ATTRIBUTES) NULL); // default security
.
.
bResult = GetMailslotInfo(ghSlot, // mailslot handle
(LPDWORD) NULL, // no maximum message size
&dw_MsgSize, // size of next message
&dw_MsgCount, // number of messages
(LPDWORD) NULL); // no read time-out
.
.
// if there are any messages then we read the mailslot
DWORD dw_MsgSize = 0;
DWORD dw_MsgCount = 0;
DWORD dw_MsgRead = 0;
LPTSTR lpszBuffer;
BOOL bResult;
HANDLE hEvent;
OVERLAPPED ov;
hEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("SigmaMailSlot"));
ov.Offset = 0;
ov.OffsetHigh = 0;
ov.hEvent = hEvent;
bResult = GetMailslotInfo(ghSlot, // mailslot handle
(LPDWORD) NULL, // no maximum message size
&dw_MsgSize, // size of next message
&dw_MsgCount, // number of messages
(LPDWORD) NULL); // no read time-out
while (dw_MsgCount != 0) // retrieve all messages
{
// memory for the message.
lpszBuffer = (LPTSTR) GlobalAlloc(GPTR,
dw_MsgSize);
if( NULL == lpszBuffer )
return FALSE;
lpszBuffer[0] = '\0';
bResult = ReadFile(ghSlot,
lpszBuffer,
dw_MsgSize,
&dw_MsgRead,
&ov);
//Write or display the message in lpszBuffer
}
//Call GetMailSlotInfo() again and repeat the above loop to get further messages.
Environment
VC++ 6.0, UNICODE, C++ and XP SP3
This sample has been tested only in Windows XP SP3.
History
- 19th May, 2009: Initial post