Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

NT Service and Inter-process Communication using Mailslot

4.40/5 (7 votes)
25 Jun 2009CPOL3 min read 34.1K   974  
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:

  1. Creating/Registering a service
  2. Removing a service
  3. Registering ServiceMain()
  4. ServiceHandler()
  5. 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:

C++
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:

C++
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():

C++
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:

C++
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:

C++
LPTSTR lpszSlotName = TEXT("<a>\\\\.\\mailslot\\sigmamain</a>");
.
.
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:

C++
#define MAILSLOTNAME <a>\\\\.\\mailslot\\sigmamain</a>
.
.
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)