Download the source code

Service applications are very common in desktop world. Before Windows CE 4.0, CE did not have the concept of service applications. From Windows CE 4.0 the Service Manager (services.exe) was added. Service Manager loads service application based on a particular registry entries on system startup or upon request from an application. A service application is a normal DLL file which exports 8 predefined function. A normal service application should export at least 5 of the required 8 functions. The other 3 are optional and only needed if you are creating a service the supports read and write operations. The function that has to be exported by a service application are:

DWORD xxx_Init(DWORD)
BOOL xxx_Deinit(DWORD)
BOOL xxx_Open(DWORD dwData,DWORD dwAccess,DWORD dwShareMode)
BOOL xxx_Close( DWORD dwData )
BOOL xxx_IOControl( DWORD dwData, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut )
DWORD xxx_Read(DWORD dwData,LPVOID pBuf,DWORD dwLen)
DWORD xxx_Write(DWORD dwData,LPCVOID pInBuf,DWORD dwInLen)
DWORD xxx_Seek(DWORD dwData,long pos,DWORD type)

The xxx represents the three letter abbreviation used by the service. This 3 letter is defined by the service and is specified in the service entry in registry. The client application used this 3 letter and an index value when trying to open using the API CreateFile.

DWORD xxx_Init(DWORD);: This function is called by the Service Manager when the service is first loaded. We can perform initialization of any resource that are used by the service application. The only parameter is the initial state of the service. This can be SERVICE_INIT_STARTED, which indicates the service is started and SERVICE_INIT_STOPPED, which indicates the service is stopped. The return value indicates success or failure, 0 indicates failure and any value greater that 0 indicates success. If the service application returns other than 0, the Service Manager will pass this value to other service functions.

BOOL xxx_Deinit(DWORD);: This function is called by the Service Manager when the service is unloaded. We can perform and resource cleanup tasks when this function is called. The only parameter is a DWORD value. This is the value that is returned by the xxx_Init function.

BOOL xxx_Open(DWORD dwData,DWORD dwAccess,DWORD dwShareMode): This is called when a client application uses CreateFile API to open the service. The first paramter will be the value that is returned by the xxx_Init function. The second parameter will be access mode used by the client application. The third parameter is the share mode used by the client application. A return value of TRUE indicates success and FALSE indicates failure.

BOOL xxx_Close( DWORD dwData ): This function is called when the client application used CloseHandle API to close the service. The only parameter is the value that returned by the xxx_Init function. TRUE indicates success and FALSE indicates failure.

BOOL xxx_IOControl( DWORD dwData, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut ): This is called when the client application uses DeviceIoControl API to pass the control codes to the service. The first parameter is the value returned by the xxx_Init function. The second parameter is the IOCTL code used by the application. The third and fourth parameter are the input buffer and it’s length. These are used by client application to pass any data to the service. The fifth and sixth parameter is the output buffer and it’s length used to pass any data to the client application from the service. The seventh parameter is the length of the data that is passed to the client application, the service should fill this parameter with the actual data that is filled in.

There are different Service IOCTL commands that can be used to control the service application, please refer to the MSDN help to study more about the common IOCTL commands.

DWORD xxx_Read(DWORD dwData,LPVOID pBuf,DWORD dwLen): This function is called when the client application used ReadFile API to read data from the service. This is an optional function and is needed when the service application supports read and write. The first parameter is the value that is returned by the xxx_Init function. Second and third parameter is the output buffer and it’s length. The service application should return the actual number of bytes that is written to the buffer.

DWORD xxx_Write(DWORD dwData,LPCVOID pInBuf,DWORD dwInLen): This is the function called when the client application uses WriteFile API to write data to the service. This is optional and only needed if the service application supports read and write. The second and third parameter is the input buffer and it’s length. The service should return the actual number of bytes written.

DWORD xxx_Seek(DWORD dwData,long pos,DWORD type): This function is called when the client application uses SetFilePointer API. The first parameter is the value returned by the xxx_Init function. The second parameter is the number of bytes to move and third parameter specifies the start of the seek operation. This can be FILE_BEGIN, FILE_CURRENT or FILE_END. This function is optional and is only needed if the service supports the seek operation.

The service application should have a registry entry under the key HKEY_LOCAL_MACHINEServices. There are different registry values to create for a service, some of them they are:

Description: Description of the service.
DisplayName: Name of the service.
Dll: DLL name of the service.
Index: Service index. This index is used along with the “Prefix” to open the service.
Keep: If this is 0 then the service will be unloaded immediately after the initialization.
Prefix: Prefix of the service DLL. This along with the index is used to open the service.

We can use the RegisterService API to register the service, but it is not recommended. It is better to manually register the service and use the ActivateService API in order to activate the service.

To open the service, we can use CreateFile API with a service file name, for example in the sample service the prefix is “UID” and the index is 1 so the file name will be “UID1:”. After obtaining the handle to the service, we can issue different IOCTL commands or read and write to the file.

The sample application presented here is a very simple service that is used to get unique device identifier. The GetDeviceUniqueID API is used to get the unique identifier. The client application starts the service and send the IOCTL_UID_SERVICE_GETID IOCTL command (which is custom IOCTL command defined by the service application) to get the unique id. The id returned by the service is displayed in an edit control.

In order to start the service you should copy the service DLL to the windows directory and run the client application. The client application register the service and activate it. An additional exported function RegisterUIDService is defined in the service DLL in order to make the registration process easy. The client application uses this function to register the service.

This is a very simple service application and I hope this will give you an introduction to the services in Windows CE.