ID: Q197820
The information in this article applies to:
This article describes how to MAPI-enable a Microsoft Windows NT service. The focus is on what considerations are necessary for MAPI to behave well inside a Windows NT Service. Therefore, creating a Windows NT service will not be discussed. For more information on creating a Windows NT service application, please see the article "Creating a Simple Windows NT Service in C++" in the October 1998 release of the Microsoft Developer Network CD. This article comes complete with an overview, detailed discussion, and a working sample of a Windows NT service.
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.
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:
-and-
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.
October 1998 Microsoft Developer Network CD article "Creating a Simple Windows NT Service in C++"
Additional query words:
Keywords : kbMAPI kbMsg kbMAPI100
Version : WINDOWS:1.0
Platform : WINDOWS
Issue type : kbhowto
Last Reviewed: December 23, 1998