MORE INFORMATION
In the article mentioned previously, the author implements a class called
CNTService. This class definition has two member methods of significance to
this topic. These methods are OnInit and Run and are defined as follows:
BOOL CMyService::OnInit()
{
// Read the registry parameters.
// Try opening the registry key:
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
// \Services\<AppName>\Parameters
HKEY hkey;
char szKey[1024];
strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\");
strcat(szKey, m_szServiceName);
strcat(szKey, "\\Parameters");
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szKey,
0,
KEY_QUERY_VALUE,
&hkey) == ERROR_SUCCESS)
{
// Yes we are installed.
DWORD dwType = 0;
DWORD dwSize = sizeof(m_iStartParam);
RegQueryValueEx(hkey,
"Start",
NULL,
&dwType,
(BYTE*)&m_iStartParam,
&dwSize);
dwSize = sizeof(m_iIncParam);
RegQueryValueEx(hkey,
"Inc",
NULL,
&dwType,
(BYTE*)&m_iIncParam,
&dwSize);
RegCloseKey(hkey);
}
// Set the initial state.
m_iState = m_iStartParam;
return TRUE;
}
void CNTService::Run()
{
DebugMsg("Entering CNTService::Run()");
while (m_bIsRunning) {
DebugMsg("Sleeping...");
Sleep(5000);
}
// Nothing more to do.
DebugMsg("Leaving CNTService::Run()");
}
The OnInit method is where the service initializes itself and does all the
preparation work for preparing to run. For our purposes, this is the most
important method because this is where you would add code to initialize the
MAPI subsystem and logon to a session.
The Run method is where all of the work of the service is done. After the
service is initialized and fully loaded into memory, this method is called.
The While loop keeps the service loaded into memory until some outside
force sets a variable - m_IsRunning - to FALSE. This is where you would add
code to perform the messaging functionality desired for this service. The
way this particular service is set up, you could add code that would send a
piece of mail every 5 seconds. There are thousands of different things that
this service could do, this is just one example of what can be done with
this particular service.
You can modify the code in the OnInit method so that as soon as the OnInit
method executes, the MAPI subsystem initialized so that when you enter the
Run method a MAPI session object is created. The modified version of this
method could look very similar to the following snippet.
NOTE: You can copy this code and replace the existing methods of the
Windows NT Service application mentioned previously. This code will not
compile or link outside the context of the article mentioned previously
without significant changes.
Sample Code
BOOL CMyService::OnInit()
{
// Read the registry parameters.
// Try opening the registry key:
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
// \Services\<AppName>\Parameters
HKEY hkey;
char szKey[1024];
strcpy(szKey, "SYSTEM\\CurrentControlSet\\Services\\");
strcat(szKey, m_szServiceName);
strcat(szKey, "\\Parameters");
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szKey,
0,
KEY_QUERY_VALUE,
&hkey) == ERROR_SUCCESS)
{
// Yes we are installed.
DWORD dwType = 0;
DWORD dwSize = sizeof(m_iStartParam);
RegQueryValueEx(hkey,
"Start",
NULL,
&dwType,
(BYTE*)&m_iStartParam,
&dwSize);
dwSize = sizeof(m_iIncParam);
RegQueryValueEx(hkey,
"Inc",
NULL,
&dwType,
(BYTE*)&m_iIncParam,
&dwSize);
RegCloseKey(hkey);
}
StartMAPI ( &m_pSession );
// Set the initial state.
m_iState = m_iStartParam;
return TRUE;
}
HRESULT CNTService::StartMAPI ( LPMAPISESSION *pSess )
{
// Declare and initialize any variables used by MAPI.
HRESULT hRes = S_OK;
MAPIINIT_0 pMapiInit = { MAPI_INIT_VERSION, MAPI_NT_SERVICE };
ULONG ulFlags = 0L;
char pProfile[64];
LPMAPISESSION pSvcSess = NULL;
// Initialize MAPI.
hRes = MAPIInitialize ( &pMapiInit );
// If MAPIInitialize succeeds, create a session object.
if ( !FAILED ( hRes ) )
{
ulFlags = MAPI_NO_MAIL |
MAPI_NEW_SESSION |
MAPI_EXPLICIT_PROFILE |
MAPI_NT_SERVICE ;
hRes = MAPILogonEx ( 0L, pProfile, NULL, ulFlags, &pSvcSess);
}
if ( !FAILED ( hRes ) )
{
*pSess = pSvcSess;
}
return hRes;
}
Because neither StartMAPI() nor m_pSession are already defined in the
CNTService class, you need to add this method and property to the class
definition so the compiler knows to expect them up front. Also, this
Windows NT Service needs to be aware of all of the special data types that
MAPI supports. To do this add the following lines of code to the class
definition found in the accompanying header file:
HRESULT StartMAPI ( LPMAPISESSION * );
LPMAPISESSION m_pSession;
Add the following line of code to the same header file in the file scope
section at the top where the other include statement is found:
#include <mapix.h>
There are a few things in this MAPI code that are not necessary for normal
desktop MAPI clients to do but are necessary for MAPI clients that run in a
Windows NT Service. The first major difference is the call to the
MAPIInitialize() function. You pass in a MAPIINIT_0 structure that lets the
MAPI subsystem know that MAPI is going to execute in a Windows NT Service.
This occurs in the OnInit because this is where all initialization should
occur. It would be confusing to move this code to the Run method because by
the time your service is in the Run method, it appears that the service has
passed all initialization tests. Receiving an initialization error in the
Run method would be misleading to the user.
The next major difference is in the call to the MAPILogonEx() fucntion. You
must specify two flags that you normally do not or should not use from a
desktop application. The two flags are:
- MAPI_NO_MAIL
-and-
- MAPI_NT_SERVICE
The MAPI_NO_MAIL flag tells the MAPI spooler, Mapisp32.exe, not to load.
The MAPI_NT_SERVICE flag lets MAPILogonEx know that the session will run in
the context of a Windows NT Service. Another important point to make is
that the lpszProfileName parameter must contain a string that maps to a
MAPI profile name that is visible to the Windows NT Service.
Windows NT Services do not, by default, run in the same security context as
an interactively logged on user. Because it runs in a different security
context, it has access to a different registry hive than an interactively
logged on user. MAPI stores all profile information for each user in
separate hives in the system registry. A Windows NT Service can only see
MAPI profiles that are created by the service itself or that have been
stored in the registry hive that maps to the security context in which the
service currently runs.
Although the preceding code does not demonstrate any MAPI calls beyond
logging on to a MAPI session, it is important to remember one following
point:
Windows NT Services are expected to run unattended.
If a function is called that displays a dialog box that requires user
input, the service effectively stops running. For this reason, it is
important to consider the MAPI calls you make. Many of these calls can
present a dialog box to the user. Each call should provide a mechanism for
preventing the display of a dialog box. Windows NT services should always
set this flag.
Even so, some service providers do not always honor their flags, and may
pop up a dialog box to prompt for user credentials or other configuration
information, particularly if that information is not stored in the profile.
Non-interactive applications must investigate carefully the MAPI provider
or providers they intend to use, to verify that the profile can be
configured programmatically and that logon can take place without unwanted
dialog boxes. This is a design issue that will not be fixed. Vigilance is
required on the part of programmers implementing MAPI service providers or
Windows NT services that use MAPI.