The following sample (MAPIRead) demonstrates retrieving an Site object
properties:
unit unMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, ExtendedMAPI,Buttons, ComCtrls;
type
TfrmMain = class(TForm)
plTOP: TPanel;
btLogOn: TSpeedButton;
btLogOff: TSpeedButton;
rgProfile: TRadioGroup;
lwUser: TListView;
procedure FormCreate(Sender: TObject);
procedure btLogOnClick(Sender: TObject);
procedure btLogOffClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
procedure InitializeMAPI;
procedure MAPILocalInit(const Flag:ULONG);
procedure MapiInternalLogOff;
procedure WhoImRef;
end;
//The function retrieves a MAPI IMAPIProp interface for the current session object.
function WhoIm(lpSession:IMAPISession; out lppCurrentObject:IMAPIProp):HRESULT;
var
frmMain: TfrmMain;
implementation
uses edkutils;
var
FMapiSession:IMAPISession; // MAPI Session variable -> Interface IMAPISession
FAdrBook:IAddrBook;
FUserProp:IMAPIProp;
const MAPI_E_TOO_MANY_SESSIONS = 8;
{$R *.DFM}
procedure TfrmMAIN.InitializeMAPI;
begin
FUserProp:=nil;
FAdrBook:=nil;
FMapiSession:=nil;
end;
procedure TfrmMAIN.MAPILocalInit(const Flag:ULONG);
var
QMess:string;
hr:HRESULT; // MAPI Finction return HRESULT
lppMAPIERRORS:pMAPIERROR;
bMapiInit:Boolean;
label cleanup;
begin
bMapiInit:=False;
hr:=MapiInitialize(nil);
(*
MAPIInitialize does not return any extended error information.
Unlike most other MAPI calls, the meanings of its return values are strictly defined
to correspond to the particular step of the initialization that failed
*)
if failed(hr) then
begin
case hr of
MAPI_E_INVALID_PARAMETER or MAPI_E_UNKNOWN_FLAGS: QMess:='Invalid
parameter or flag!';
MAPI_E_TOO_COMPLEX: QMess:='The keys required by MAPI could not be initialized.';
MAPI_E_VERSION: QMess:='The version of OLE installed on the workstation is not compatible with this version of MAPI.';
MAPI_E_SESSION_LIMIT: QMess:='MAPI sets up context specific to the current process.'+#13+'Failures may occur on Win16 if the number of processes exceeds a certain number,'+#13+ 'or on any system if available memory is exhausted.';
MAPI_E_NOT_ENOUGH_RESOURCES: QMess:='Not enough system resources were available to complete the operation.';
MAPI_E_INVALID_OBJECT: QMess:='May fail if system resources are exhausted.';
MAPI_E_NOT_INITIALIZED: QMess:='The MAPI profile provider has encountered an error.';
else QMess:='The MAPI Error!'
end;
MessageBox(0, PCHAR(QMess),'MS MAPI Subsystem', MB_OK or MB_ICONERROR);
goto cleanup;
end;
(*
The MAPILogonEx function logs a client application on to
a session with the messaging system.
function MAPILogonEx (ulUIParam : ULONG; lpszProfileName : PChar;
lpszPassword : PChar; ulFlags : ULONG;
out lppSession : IMAPISession) : HResult; stdcall;
Parameters
ulUIParam - [input] Handle to the window to which the logon dialog box is modal. If no dialog box is displayed during the call, the ulUIParam parameter is ignored. This parameter can be zero.
lpszProfileName - [input] Pointer to a string containing the name of the profile to use when logging on. This string is limited to 64 characters.
lpszPassword - [input] Pointer to a string containing the password of the profile. The lpszPassword parameter can be NULL whether or not the lpszProfileName parameter is NULL. This string is limited to 64 characters.
flFlags - [input] Bitmask of flags used to control how logon is performed. The following flags can be set:
MAPI_ALLOW_OTHERS - The shared session should be returned, allowing subsequent clients to acquire the session without providing any user credentials.
MAPI_EXPLICIT_PROFILE - The default profile should not be used, and the user should be required to supply a profile.
MAPI_EXTENDED - Log on with extended capabilities. This flag should always be set. The older MAPILogon function is no longer available.
MAPI_FORCE_DOWNLOAD - An attempt should be made to download all of the user's messages before returning. If the MAPI_FORCE_DOWNLOAD flag is not set, messages can be downloaded in the background after the call to MAPILogonEx returns.
MAPI_LOGON_UI - A dialog box should be displayed to prompt the user for logon information if required. When the MAPI_LOGON_UI flag is not set, the calling client does not display a logon dialog box and returns an error value if the user is not logged on. MAPI_LOGON_UI and MAPI_PASSWORD_UI are mutually exclusive.
MAPI_NEW_SESSION - An attempt should be made to create a new MAPI session rather than acquire the shared session. If the MAPI_NEW_SESSION flag is not set, MAPILogonEx uses an existing shared session even if the lpszprofileName parameter is not NULL.
MAPI_NO_MAIL - MAPI should not inform the MAPI spooler of the session's existence. The result is that no messages can be sent or received within the session except through a tightly coupled store and transport pair. A calling client sets this flag if it is acting as an agent, if configuration work must be done, or if the client is browsing the available message stores.
MAPI_NT_SERVICE - The caller is running as a Windows NT service. Callers that are not running as a Windows NT service should not set this flag; callers that are running as a service must set this flag.
MAPI_PASSWORD_UI - A dialog box should be displayed to prompt the user for the profile password. MAPI_PASSWORD_UI cannot be set if MAPI_LOGON_UI is set because the calling client can only present one of the two dialog boxes. This dialog box does not allow the profile name to be changed; the lpszProfileName parameter must be non-NULL.
MAPI_SERVICE_UI_ALWAYS - MAPILogonEx should display a configuration dialog box for each message service in the profile. The dialog boxes are displayed after the profile has been chosen but before any message service is logged on. The MAPI common dialog box for logon also contains a check box that requests the same operation.
MAPI_TIMEOUT_SHORT - The logon should fail if blocked for more than a few seconds.
MAPI_UNICODE - The passed-in strings are in Unicode format. If the MAPI_UNICODE flag is not set, the strings are in ANSI format.
MAPI_USE_DEFAULT - The messaging subsystem should substitute the profile name of the default profile for the lpszProfileName parameter. The MAPI_EXPLICIT_PROFILE flag is ignored unless lpszProfileName is NULL or empty.
lppSession - [out] Pointer to a pointer to the MAPI session interface.
*)
hr:=MAPILogonEx(0, nil, nil,
MAPI_EXTENDED or
MAPI_NEW_SESSION or
flag,
FMapiSession);
if failed(hr) then
begin
case hr of
MAPI_E_LOGON_FAILED: MessageDlg(WrapText('The logon did not succeed, either
because one or more of the parameters to Profile were invalid or because there were too many sessions open already.',#13#10, ['.',' '], 42), mtError, [mbOk], 0);
MAPI_E_TIMEOUT: MessageDlg(WrapText('MAPI serializes all logons through a mutex. This is returned if the another thread held the mutex.',#13#10, ['.',' '], 42), mtError, [mbOk], 0);
MAPI_E_USER_CANCEL: ShowMessage(WrapText('The user canceled the operation, typically by choosing the Cancel button in a dialog box.',#13#10, ['.',' '], 42));
MAPI_E_TOO_MANY_SESSIONS: MessageDlg(WrapText('The user had too many sessions open simultaneously. No session handle was returned.',#13#10, ['.',' '], 42), mtError, [mbOk], 0);
MAPI_E_UNCONFIGURED: MessageDlg(WrapText('A service provider has not been configured, and therefore the operation did not complete.',#13#10, ['.',' '], 42),mtError, [mbOk], 0);
else
MessageDlg(WrapText('The logon did not succeed',#13#10, ['.',' '], 42),mtError, [mbOk], 0);
end;
goto cleanup;
end;
// Open address book
hr := FMapiSession.OpenAddressBook(0, nil, AB_NO_DIALOG, FAdrBook);
if failed(hr) then
begin
lppMAPIERRORS:=nil;
FMAPISession.GetLastError(hr,0,lppMAPIERRORS);
if assigned(lppMAPIERRORS) then
begin
if Assigned(lppMAPIERRORS.lpszError) then
MessageBox(0,lppMAPIERRORS.lpszError,
lppMAPIERRORS.lpszComponent,
MB_OK or MB_ICONERROR);
lppMAPIERRORS:=nil;
end;
goto cleanup;
end;
bMapiInit:=True;
cleanup:
btLogOn.Enabled:=bMapiInit=False;
btLogOff.Enabled:=bMapiInit;
if ((btLogOff.Enabled) and Assigned(FMapiSession)) then
WhoImRef;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
InitializeMAPI;
end;
procedure TfrmMain.MapiInternalLogOff;
begin
lwUser.Items.Clear;
if Assigned(FAdrBook) then FAdrBook:=nil;
if Assigned(FUserProp) then FUserProp:=nil;
if Assigned(FMapiSession) then
begin
(*
The IMAPISession.Logoff method ends a MAPI session.
Parameters
ulUIParam - [input] Handle of the parent window for any dialog boxes or windows to be displayed if possible. This parameter is ignored if the MAPI_LOGOFF_UI flag is not set.
ulFlags - [input] Bitmask of flags that control the logoff operation. The following flags can be set:
MAPI_LOGOFF_SHARED - If this session is shared, all clients logged on using the shared session should be notified of the logoff in progress. The clients should log off. Any client using the shared session can set this flag. MAPI_LOGOFF_SHARED is ignored if the current session is not shared.
MAPI_LOGOFF_UI - Logoff can display a dialog box during the operation, possibly prompting the user for confirmation.
ulReserved - Reserved; must be zero.
The IMAPISession.Logoff method ends a MAPI session.
When Logoff returns, none of the methods except
for Release (IMapiSession:=nil) can be called.
*)
FMapiSession.Logoff(0,0,0);
FMapiSession:=nil;
(*
The MAPIUninitialize function decrements the reference count,
cleans up, and deletes per-instance global data
for the MAPI32.DLL.
A client application calls the MAPIUninitialize function
to end its interaction with MAPI, begun with a call to the MAPIInitialize
function. After MAPIUninitialize is called,
no other MAPI calls can be made by the client.
MAPIUninitialize decrements the reference count,
and the corresponding MAPIInitialize function increments
the reference count.
Thus, the number of calls to one function must equal
the number of calls to the other.
*)
MAPIUninitialize;
end;
end;
procedure TfrmMain.btLogOnClick(Sender: TObject);
begin
case rgProfile.ItemIndex of
0: MAPILocalInit(MAPI_USE_DEFAULT);
1: MAPILocalInit(MAPI_LOGON_UI);
end;
end;
procedure TfrmMain.btLogOffClick(Sender: TObject);
begin
MapiInternalLogOff;
btLogOn.Enabled:=True;
btLogOff.Enabled:=False;
end;
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
MapiInternalLogOff;
end;
procedure TfrmMAIN.WhoImRef;
const
PR_EMS_AB_HOME_MDB = $8006001E;
var
HR:HRESULT;
lppMAPIERRORS:pMAPIERROR;
lPropTagArray:PSPropTagArray;
lPSPropValue:PSPropValueArray;
lpHOMEMDB:PSPropValue;
I, cValues:ULONG;
ListItem:TListItem;
Save_Cursor:TCursor;
pszSiteDN:PCHAR;
lpulObjType,
cbeid:ULONG;
peid:PENTRYID;
SiteContainer:IMAPIContainer;
label cleanup;
begin
Save_Cursor:=Screen.Cursor;
Screen.Cursor:=crHourGlass;
FUserProp:=nil;
lPropTagArray:=nil;
lPSPropValue:=nil;
lpHOMEMDB:=nil;
pszSiteDN:=nil;
peid:=nil;
SiteContainer:=nil;
// Get IMAPIPROP for Loged Object
hr:=WhoIm(FMAPISession,FUserProp);
if failed(hr) then
begin
//Error
processing....
goto cleanup;
end;
hr:=HrGetOneProp(FUserProp,PR_EMS_AB_HOME_MDB,lpHOMEMDB);
if failed(hr) then
begin
//Error
processing....
goto cleanup;
end;
//Finding Exchange
Site DN
if Assigned(lpHOMEMDB) then
if lpHOMEMDB.ulPropTag = PR_EMS_AB_HOME_MDB then
begin
GetMem(pszSiteDN,StrLen(IGetSiteDN(lpHOMEMDB.Value.lpszA))+1);
StrCopy(pszSiteDN,IGetSiteDN(lpHOMEMDB.Value.lpszA));
end
else goto cleanup
else goto cleanup;
hr:=HrCreateDirEntryIdEx(FAdrBook,
pszSiteDN,
cbeid,
peid);
if failed(hr) then
begin
//Error
processing....
goto cleanup;
end;
//Opening Site Container
lpulObjType:=0;
hr:=FAdrBook.OpenEntry(cbeid,
peid,
nil,
MAPI_DEFERRED_ERRORS,
lpulObjType,
IUnknown(SiteContainer));
if failed(hr) then
begin
//Error
processing....
goto cleanup;
end;
//The IMAPIProp.GetPropList method returns property tags for all properties.
(*
Parameters
ulFlags - [input] Bitmask of flags that controls the format for the strings in the returned property tags. The following flag can be set:
MAPI_UNICODE - The returned strings are in Unicode format. If the MAPI_UNICODE flag is not set, the strings are in ANSI format.
lppPropTagArray - [out] Pointer to a pointer to the property tag array containing tags for all of the object's properties.
The IMAPIProp.GetPropList method retrieves the property tag
for each property currently supported by an object.
If the object does not currently support any properties,
GetPropList returns a property tag array with the cValues member set to zero.
*)
cValues:=0;
hr := SiteContainer.GetPropList(0,lPropTagArray);
if Assigned(lPropTagArray) then
cValues:=lPropTagArray.cValues;
if failed(hr) then
begin
//Error
processing....
goto cleanup;
end;
//The IMAPIProp.GetProps method retrieves the property value of one
//or more properties of an object.
(*
Parameters
lpPropTagArray - [in] Pointer to an array of property tags identifying the properties whose values are to be retrieved.
The lpPropTagArray parameter must either be NIL, indicating that values for all properties of the object should be returned, or point to an SPropTagArray structure containing one or more property tags.
ulFlags - [in] Bitmask of flags that indicates the format for properties that have the type PT_UNSPECIFIED. The following flag can be set:
MAPI_UNICODE - The string values for these properties should be returned in the Unicode format. If the MAPI_UNICODE flag is not set, the string values should be returned in the ANSI format.
lpcValues - [out] Pointer to a count of property values pointed to by the lppPropArray parameter. If lppPropArray is 0, the contents of the lpcValues parameter is zero.
lppPropArray - [out] Pointer to a pointer to the retrieved property values.
*)
lPSPropValue:=nil;
hr := SiteContainer.GetProps(lPropTagArray,0,cValues,lPSPropValue);
if failed(hr) then
begin
//Error
processing....
goto cleanup;
end;
For I:=0 to cValues-1 do
begin
ListItem:=lwUser.Items.Add;
ListItem.Caption:=IntToHEX(lPSPropValue[I].ulPropTag,8);
case LoWord(lPSPropValue[I].ulPropTag) of
PT_SHORT : begin
ListItem.SubItems.Add('SHORT');
ListItem.SubItems.Add(VarToStr(PT_SHORT2V(lPSPropValue[I].Value)));
end;
PT_LONG : begin
ListItem.SubItems.Add('LONG');
ListItem.SubItems.Add(VarToStr(PT_LONG2V(lPSPropValue[I].Value)));
end;
.... etc....
end; //case
end;
cleanup:
if Assigned(SiteContainer) then SiteContainer:=nil;
if Assigned(peid) then MAPIFreeBuffer(peid);
if Assigned(pszSiteDN) then FreeMem(pszSiteDN);
if Assigned(lpHOMEMDB) then MAPIFreeBuffer(lpHOMEMDB);
if Assigned(lPSPropValue) then MAPIFreeBuffer(lPSPropValue);
if Assigned(lPropTagArray) then MAPIFreeBuffer(lPropTagArray);
if Assigned(FUserProp) then FUserProp:=nil;
Screen.Cursor:=Save_Cursor;
end;
function WhoIm(lpSession:IMAPISession;
out lppCurrentObject:IMAPIProp):HRESULT;
(*
The function retrieves a MAPI IMAPIProp interface
for the current session object.
Parameters
lpSession - Input parameter. Points to a MAPI IMAPISession interface containing the MAPI session.
lppCurrentObject - Output parameter. Points to a MAPI IMAPIProp interface containing the current object.
*)
var
hr:HRESULT;
cbSessionEntryID:ULONG;
pSessionEntryID:PENTRYID;
pCurrentObjectInterface:IMAPIPROP;
ulObjType :ULONG;
lppMAPIError:pMAPIError;
label cleanup;
begin
lppMAPIError:=nil;
cbSessionEntryID:=0;
pSessionEntryID:=nil;
pCurrentObjectInterface:=nil;
// Get the entry ID for the session.
(*
The IMAPISession.QueryIdentity method returns
the entry identifier of the object that provides the primary identity
for the session.
Parameters
cbSessionEntryID - [out] Pointer to the count of bytes in the entry identifier pointed to by the lppEntryID parameter.
pSessionEntryID - [out] Pointer to a pointer to the entry identifier of the object providing the primary identity.
*)
hr := lpSession.QueryIdentity(cbSessionEntryID,pSessionEntryID);
if failed(hr) then
begin
//Error
processing....
goto cleanup;
end;
// Open the entry ID and get an IMAPIProp interface.
ulObjType := 0;
hr := lpSession.OpenEntry(cbSessionEntryID,
pSessionEntryID,
IID_IMAPIProp,
MAPI_BEST_ACCESS or MAPI_DEFERRED_ERRORS,
ulObjType,
IUNKNOWN(pCurrentObjectInterface));
if failed(hr) then
begin
//Error
processing....
goto cleanup;
end;
// Everything was successful, so return the interface to the caller.
lppCurrentObject := pCurrentObjectInterface;
cleanup:
if hr<>S_OK then
begin
pCurrentObjectInterface:=nil;
lppCurrentObject := Nil;
end;
if Assigned(pSessionEntryID) then MAPIFREEBUFFER(pSessionEntryID);
result :=hr;
end;
end.
Note that I logon (MAPILogonEx) to MS Exchange server profile after initializing MAPI. Then I open address book and obtain entry ID for my Site object. Having done that, I open the object (OpenEntry) and obtain all its properties (GetProps).
After running the above fragment in the debugger on my Exchange server system, I have seen 10 properties dumped to the screen.
After seeing such dump you will notice right away the difference between DAPI and MAPI approaches. The number of MAPI properties is different from the number of DAPI attributes obtained from the same object. Apparently, two quite different naming conventions are used here.
There exists a relationship between attribute names and MAPI property tags. Microsoft Exchange Server Programmer's Reference comes with a subsection named "Directory Schema Attributes" in its "Reference" section, where a lot of attributes are listed with their corresponding MAPI IDs. For example, for Obj-Dist-Name attribute MapiID is $803C. This corresponds to higher 16 bits in MAPI property tag - MAPI property ID. Thus, for any attribute name it is easy to determine MAPI property ID (if one exists) by just looking into appropriate reference section.
You may wonder why some attributes don't have MAPI IDs. An example would be
the "Governs-ID" attribute. This attribute defines a specific
relationship between directory schema objects. Directory schema does not define
any physical objects (such as a mailbox, for example), but rather their
hierarchy, or collection of types and how they are related to each other. It
would, perhaps, be correct to say that MAPI was not designed to encapsulate
schema definition. This is the reason why some directory attributes don't have
associated MAPI IDs.
More information and examples
about using Extended MAPI with DELPHI 5
you can find HERE