Service registration occurs when the application calls ctaInitialize. Two binding functions are called during initialization:
Service registration
ctaInitialize can only be called once per process; therefore the call will be thread-safe. xxxInitializeManager and xxxDefineService do not need to be coded to be reentrant since ctaInitialize can only be called once per process (and only from one thread within that process).
If the service manager is not loaded, ctaInitialize loads it into memory. Each service manager must reside in its own DLL or library that conforms to the naming convention xxxmgr.dll (for example, tikmgr.dll) or libxxxmgr.so in UNIX.
The dispatcher then calls the service manager entry point xxxInitializeManager.
xxxInitializeManager is the only required exported function from a service manager DLL or shared library. xxxInitializeManager performs the following functions:
Declares version numbers and compatibility levels for service manager
The binding function names are registered with the dispatcher during service initialization. This registration makes the binding functions of the service manager available to the dispatcher to be invoked when needed. Binding functions are declared in the CTASVCMGR_FNS function pointer table. A NULL entry in the table indicates that the default implementation for that function should be used (that is, no service-specific implementation of the binding function is required). The following table lists the default implementation of each binding function:
|
Binding function |
Default implementation |
|---|---|
|
Return SUCCESS | |
|
Return SUCCESS | |
|
Return SUCCESS | |
|
Return SUCCESS | |
|
Return SUCCESS | |
|
Enqueue CTAEVN_DISP_OPEN_SERVICE_DONE event, reason CTA_REASON_FINISHED | |
|
Enqueue CTAEVN_DISP_CLOSE_SERVICE_DONE event, reason CTA_REASON_FINISHED | |
|
Return SUCCESS | |
|
Return SUCCESS | |
|
Return SUCCESS | |
|
Return SUCCESS | |
|
Return NULL | |
|
Format event or command by printing the IDs | |
|
Return SUCCESS | |
|
Return SUCCESS | |
|
xxxGetFunctionPointer |
Return NULL |
tik service
<<<< excerpt from tikbnd.c >>>>
STATIC CTASVCMGR_FNS tikSvcmgrFns =
{
sizeof(CTASVCMGR_FNS),
tikDefineService, /* xxxDefineService() */
tikAttachServiceManager, /* xxxAttachServiceManager() */
tikDetachServiceManager, /* xxxDetachServiceManager() */
tikOpenServiceManager, /* xxxOpenServiceManager() */
tikCloseServiceManager, /* xxxCloseServiceManager() */
tikOpenService, /* xxxOpenService() */
tikCloseService, /* xxxCloseService() */
NULL, /* xxxProcessEvent() */
tikProcessCommand, /* xxxProcessCommand() */
NULL, /* xxxAddRTC() */
NULL, /* xxxRemoveRTC() */
tikGetText, /* xxxGetText() */
tikFormatMessage, /* xxxFormatMessage() */
tikSetTraceLevel, /* xxxSetTraceLevel() */
tikFormatTraceBuffer, /* xxxFormatTraceBuffer() */
NULL, /* xxxGetFunctionPointer() -future */
};
There are two types of version information for a Natural Access service:
Version ID
Compatibility level
The version ID consists of revision information that is intended to be for human-readable identification purposes. The version information consists of a revision number and a build date.
The revision number is reported in the format major_rev.minor_rev (for example, 2.2). The major revision number is typically incremented when a major change in functionality is released. The minor revision number is typically incremented when small changes (for example, bug fixes) are released. Typically, the minor revision number is reset to zero when the major revision number is incremented.
The build date is in mm dd yyyy format. It is typically encoded using the __DATE__ compiler built-in.
Compatibility levels are used for runtime interoperability checking between a target software module and a caller of that software module. For example, the compatibility level of the dispatcher (acting as the target software module) may be checked by a Natural Access service (acting as a caller).
A compatibility level consists of a 1-based integer that is assigned to a target software module. This level is incremented when the software module is modified such that it is no longer compatible with existing clients. Examples of such modifications include:
Changes to the signature of a public function call
Removal of a public function call
There are three interoperability scenarios to consider when writing a Natural Access service:
Dispatcher to Natural Access service
Natural Access service API to Natural Access application
Natural Access service SPI to another Natural Access service
The following table describes processing aspects of these scenarios:
|
Function call |
Assigning a compatibility level |
Requesting a compatibility level |
Comparing assigned to requested compatibility levels |
|---|---|---|---|
|
Dispatcher function called from Natural Access service |
Done internally by Natural Access; transparent to service writer. |
Use the DISP_COMPATLEVEL macro as the value for reqdisplevel in CTAINTREV_INFO structure |
dispRegisterServiceManager compares the internal value of the dispatcher with the value in reqdisplevel. If they are equal, processing continues. Otherwise, processing aborts. |
|
Natural Access service function called from Natural Access application |
Set the exapilevel field of the CTAINTREV_INFO structure. |
Get the XXXAPI_COMPATLEVEL macro from the target's xxxdef.h file. |
Write an application function to call ctaGetServiceVersion for the target service. Compare XXXAPI_COMPATLEVEL with the compatlevel field returned in the CTA_REV_INFO structure. |
|
Natural Access service function called from another Natural Access service |
Set the exspilevel field of the CTAINTREV_INFO structure. |
Get the XXXSPI_COMPATLEVEL macro from the target's xxxdef.h file. In the client's xxxDefineService function, create a CTAREQSVC_INFO structure and set the target reqspilevel field to the target's XXXSPI_COMPATLEVEL. |
dispRegisterService compares the client's reqspilevel with the targets exspilevel. If they are equal, processing continues. Otherwise, processing aborts. |
To specify compatibility levels and version IDs for a service:
xxxdef.h should contain the four macros: XXX_MAJORREV, XXX_MINORREV, XXXAPI_COMPATLEVEL, XXXSPI_COMPATLEVEL
xxxbnd.c should initialize the appropriate CTAINTREV_INFO fields for the service manager in xxxInitializeManager. Initialize the appropriate CTAINTREV fields for the service implementation in xxxDefineService.
The CTAINTREV_INFO structure is defined as follows:
typedef struct /* CTA internal revision info structure */
{
DWORD size; /* Size of the returned structure */
DWORD majorrev; /* Major revision of service/manager */
DWORD minorrev; /* Minor revision of service/manager */
char builddate [12]; /* Build date, "Mmm dd yyyy\0" */
DWORD reqdisplevel; /* Required compat level of Dispatcher */
DWORD expapilevel; /* Exported compat level of Service API */
DWORD expspilevel; /* Exported compat level of Service SPI */
} CTAINTREV_INFO;
tik service
<<<< excerpt from tikdef.h >>>>
-----------------------------------------------------
TIK Version IDs
- These ID's are used to identify the revision level of
this service.
------------------------------------------------------------*/
#define TIK_MAJORREV 1
#define TIK_MINORREV 0
/*------------------------------------------------------------
TIK Compatibility Level IDs
- These ID's are used to determine runtime compatibility
between the installed TIK service and clients of the TIK
service.
- API_COMPATLEVEL is used by Natural Access application
developers who want to check the "hard coded" value in
their application against the "hard coded" value in the
installed TIK service
- SPI_COMPATLEVEL is useful to Natural Access service
writers who want to check the "hard coded" value in
their client service against the "hard coded" value in
the installed TIK service.
-----------------------------------------------------------*/
#define TIKAPI_COMPATLEVEL 1
#define TIKSPI_COMPATLEVEL 1
Control tracing in one of two ways: with ctdaemon, where a tracemask can be set using the ctdaemon command processor, or with an internal, service-specific tracemask that can be set within an application.
With two mechanisms to control tracing, an application developer is able to:
Trace a service on a global basis across multiple applications. By setting the global tracemask to a particular value, the tracemask is seen by all instances of a service in all applications.
Trace a service on an application specific basis by calling ctaSetTraceLevel for a specific service in a specific application on a per context basis.
To determine whether to generate a trace record associated with a trace category, the service must check both the global tracemask and the local (service-specific) tracemask.
To check the global tracemask, the service, upon initialization, must first call dispGetTracePointer to acquire a pointer to the global tracemask. It then stores this pointer so it can be accessed through all service manager and service implementation functions.
tik service
<<<< excerpt from tikbnd.c >>>>
/* ---------------------------------------------
Global service trace mask
-----------------------------------------------*/
volatile DWORD *tikTracePointer;
To define a local tracemask, define a tracemask on a per context basis. A tracemask is defined as a DWORD.
tik service
<<<< excerpt from tikdef.h >>>>
/* ----------------------------------------------------------
A DWORD tracemask is included in the definition of a
TIK_CHANNEL_OBJECT since it is allocated (as a mgrcontext) on a
per CTA context basis.
----------------------------------------------------------*/
typedef struct _TIK_CHAN
{
DWORD size; /* Size of this structure */
DWORD timestamp; /* Context creation timestamp. */
CTAHD ctahd; /* Opening CTA context handle */
DWORD channelId; /* Opened channel number. */
DWORD state; /* current state of channel. */
DWORD owner; /* Current owner of channel. */
DWORD tracemask; /* Context specific tracemask */
TIK_DEVICE_OBJECT *ptikDevice; /* Pointer to "tick device" object. */
TIK_CHANNEL_INFO channel_info; /* Channel-specific info. */
} TIK_CHANNEL_OBJECT;
tik service
<<<< excerpt from tikbnd.c >>>>
DWORD NMSAPI tikInitializeManager( void )
{
DWORD ret;
CTABEGIN("tikInitializeManager");
static BOOL initialized = FALSE;
if (initialized)
{
return CTALOGERROR(NULL_CTAHD, CTAERR_ALREADY_INITIALIZED, TIK_SVCID);
}
/* Set manager revision information. */
tikMgrRevInfo.size = sizeof(CTAINTREV_INFO);
tikMgrRevInfo.majorrev = TIK_MAJORREV;
tikMgrRevInfo.minorrev = TIK_MINORREV;
strcpy (tikMgrRevInfo.builddate, tikBuildDate);
tikMgrRevInfo.expapilevel = TIKAPI_COMPATLEVEL;
tikMgrRevInfo.expspilevel = TIKSPI_COMPATLEVEL;
tikMgrRevInfo.reqdisplevel = DISP_COMPATLEVEL;
dispGetTracePointer(&tikTracePointer);
ret = dispRegisterServiceManager("TIKMGR", &tikSvcmgrFns,
&tikMgrRevInfo);
if (ret != SUCCESS)
{
return CTALOGERROR(NULL_CTAHD, ret, TIK_SVCID);
}
initialized = TRUE;
return SUCCESS;
}
After initializing a service manager and its binding functions, services are initialized. The dispatcher initializes each service specified in the call to ctaInitialize by invoking the appropriate xxxDefineService binding function.
xxxDefineService performs the following tasks:
Declares the parameter descriptor tables.
Declares the version numbers for the service.
Requests SPI compatibility level checking for other services whose SPI will be called from this service.
The dispatcher manages parameters for registered services. During service registration (during the call to ctaInitialize), the service must pass the pointer to the parameter description table defined in xxxparm.c to the dispatcher through dispRegisterService.
After the parameter table is registered with the dispatcher, it must not be modified. Each individual parameter descriptor must remain valid and unmodified. All dynamic changes to parameter descriptor tables and their contents must take place before service registration.
tik service
<<<< excerpt from tikbnd.c >>>>
STATIC DWORD NMSAPI tikDefineService( char* svcname )
{
DWORD ret;
/*
* Needed by Natural Access provided error logging
* service
*/
CTABEGIN("tikDefineService");
/* Set service revision info. */
tikSvcRevInfo.size = sizeof(CTAINTREV_INFO);
tikSvcRevInfo.majorrev = TIK_MAJORREv;
tikSvcRevInfo.minorrev = TIK_MINORREV;
tikSvcRevInfo.expapilevel = TIKAPI_COMPATLEVEL;
tikSvcRevInfo.expspilevel = TIKSPI_COMPATLEVEL;
tikSvcRevInfo.reqdisplevel = DISP_COMPATLEVEL;
strcpy(tikSvcRevInfo.builddate, __DATE__);
/* The most important phase of initializing a service! */
ret = (dispRegisterService("TIK",
TIK_SVCID,
"TIKMGR",
&tikSvcRevInfo,
NULL,
0,
_tikParmDescTable,
NULL));
if ( ret != SUCCESS )
{
return CTALOGERROR(NULL_CTAHD, ret, TIK_SVCID);
}
return SUCCESS;
}