|
How to create MS Exchange Profiles in MS Outlook with MAPI
For a MAPI expert these articles may seem trivial, but for a novice this may
save a bunch of time and allows the programming task to be concentrated on,
rather than fighting with Exchange profiles and MAPI interfaces.
I'll take up a program development process that will create and update an MS
Exchange profile in MS Outlook using the MAPI IProfAdmin interface. When I
started it, the information was not available from a single source. I have tried
to collect all the stuff here. If you are new to MAPI programming, you'll find
this a simple explanation of MAPI basics.
There are two programs available from Microsoft: <NewProf.exe> and <ModProf.exe>
on the subject. The first one creates a new profile or overwrites an existing
one using definitions in .prf file. The second utility may create a new profile
if it doesn't exist just like <NewProf> does, or may append information to an
existing profile. These two utilities are useful in most cases, but sometimes
one may need something special. Ok, my task was to make a utility that would be
able to create or update MS Exchange profiles in MS Outlook.
Let’s examine work with MAPI. What we need to do first is to find out if the
profile already exists. So we need to
- Initialize MAPI;
- Get an IProfAdmin interface;
- Get a pointer to a Profile table that contains all profiles. Each profile
occupies one row.
- Now you have a table of profiles and may use HrQueryAllRows() function to
filter row(s) that you need.
- If you get at least one row that means that the profile exists.
The result of this program can be viewed in Settings/Control Panel/Mail. Or
you may download Microsoft ProfMan2
here. It has source code that will also help
you better understand the subject.
Now let’s explore this in detail. We’ll need the following variables:
- HRESULT hr;
- LPMAPITABLE pTable = NULL;
- LPSRowSet
pRows = NULL;
- SRestriction sres;
- SPropValue spv;
- bool found=false;
- char *pszProfileName;
//Name of profile we
want to create (update)
- SizedSPropTagArray(1, Columns) ={1, {PR_DISPLAY_NAME}};
To initialize MAPI you just need to write:
- hr = MAPIInitialize(NULL);
- where hr is variable of type HRESULT. You may check it with FAILED() macros:
-
- if (!FAILED(hr))
- {
- //Get IProfAdmin interface
- hr = MAPIAdminProfiles(0, &pProfAdmin);
- if (!FAILED(hr))
- {
- // Get the ProfileTable - Contains all profiles
- hr = pProfAdmin->GetProfileTable(0, &pTable);
- if (!FAILED(hr))
- {
- <see code below>
- }
- }
- }
Ok now we have pointer to the table of pointers, let’s make a filter:
- sres.rt = RES_CONTENT; // we will search a property value for specific
contents
- sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
//full string match
- sres.res.resContent.ulPropTag = PR_DISPLAY_NAME;
//search in “display name”
column
- sres.res.resContent.lpProp = &spv;
//condition
- spv.ulPropTag = PR_DISPLAY_NAME;
// where “display name” == spv.Value.lpszA
- spv.Value.lpszA = pszProfileName;
//name of profile
-
- //use the filter to get row(s) where the display name equals our profile
name:
- hr = HrQueryAllRows(pTable,
//pointer to table of pointers
- (LPSPropTagArray) &Columns,
//list of columns we will get
- &sres, //filter
- NULL,
//we don’t need any sorting here
- 0,
//retrieve all rows that match
- &pRows);
//pointer to resulting table
Now I’ll
explain the tables.
|
Name of column |
PR_DISPLAY_NAME |
PR_INSTANCE_NAME |
PR_DEFAULT_PROFILE |
|
Value |
Profile name that displays in profiles
list |
UID – Unique Identifier of the row |
true/false |
|
Type |
PT_STRING8 |
PT_BINARY |
PT_BOOLEAN |
Table 1.1
Profile table.
So the code above without the filter will return a table with
one column “PR_DISPLAY_NAME” and rows with profile names:
|
PR_DISPLAY_NAME |
|
Profile_Name_1 |
|
… |
|
Profile_Name_n |
Table 1.2
Result table with res=NULL
- pRows (in our code) is a structure of SRowSet type:
- typedef struct SRowSet
- {
- ULONG cRows;
//contains number of rows
- SRow aRow[MAPI_DIM];
- };
- typedef struct SRow
- {
- ULONG ulAdrEntryPad;
- ULONG cValues;
//Count of property values pointed to by
lpProps
- LPSPropValue lpProps;
//Pointer to an array of SPropValue structures that
describes the property
-
//values for the columns in the row.
- };
- typedef struct SPropValue
- {
- ULONG ulPropTag;
//Property tag for the property. Property tags are 32-bit
unsigned integers
-
//consisting of the property's unique identifier in the high-order 16 bits
-
//and the property's type in the low-order 16 bits.
- ULONG dwAlignPad;
- union _PV Value;
- };
I think the ulPropTag needs more explanation. This is the variable that
contains our column names such as “PR_DISPLAY_NAME”. PR_DISPLAY_NAME is a
constant 32-bit unsigned integer. In the low-order 16 bits it has the
property type (third row of Table 1.1). As there are many types present we have
Value union that
contains variables of different types – one variable for each appropriate
type. Here are some of the most-used Value variables:
|
Property type |
Value |
Data type of Value |
|
PT_LONG (unsigned) |
ul |
ULONG |
|
PT_FLOAT |
flt |
float |
|
PT_DOUBLE |
dbl |
double |
|
PT_BOOLEAN |
b |
unsigned
short int |
|
PT_SYSTIME |
ft |
FILETIME |
|
PT_STRING8 |
lpszA |
LPSTR |
|
PT_BINARY |
bin |
BYTE
[array] |
|
PT_UNICODE |
lpszW |
LPWSTR |
|
PT_CLSID |
lpguid |
LPGUID
|
Table 1.3 Some property types
- So now you understand this code:
-
- spv.ulPropTag = PR_DISPLAY_NAME;
// where “display name” == spv.Value.lpszA
- spv.Value.lpszA = pszProfileName;
//name of profile
-
- As PR_DISPLAY_NAME has the PT_STRING8 type we assign pszProfileName to lpszA.
For example if we need to get the default profile we should search by the
- PR_DEFAULT_PROFILE column:
- spv.ulPropTag =
PR_DEFAULT_PROFILE; //where “default profile” == spv.Value.b
- spv.Value.b =
true; //attribute
The property’s unique identifier is in high-order 16 bits of the PR_...
constant. Something like 0x6600. Values of unique identifiers may be found at
[1].
Let’s return to our project:
- if (!FAILED(hr) && pRows->cRows>0)
//result of HrQueryAllRows
- {
- found=true;
- cout<<"Found!"<<endl;
- }
Now, as we finish with the code don’t forget to release interfaces and free
memory:
- //Cleanup
- if (pRows) FreeProws(pRows);
- if (pTable) pTable->Release();
- if (pProfAdmin) pProfAdmin->Release();
- MAPIUninitialize();
So here we are. More information on this subject maybe found in the book
“Inside MAPI” by Irving De la Cruz and Les Thaler and in MSDN.
Next article will be devoted to deleting and creating a profile.
- References:
- [1] “Microsoft Exchange Property Tags”, MSDN
RETURN to
my Article Index
 |