Become a Columnist Microsoft Exchange Site Microsoft Support SiteMSDN Exchange Site
Subscribe to OutlookExchange
Anderson Patricio
Ann Mc Donough
Bob Spurzem
Brian Veal
Catherine Creary
Cherry Beado
Colin Janssen
Collins Timothy Mutesaria
Drew Nicholson
Fred Volking
Glen Scales
Goran Husman
Guy Thomas
Henrik Walther
Jason Sherry
Jayme Bowers
John Young
Joyce Tang
Justin Braun
Konstantin Zheludev
Kristina Waters
Kuang Zhang
Mahmoud Magdy
Martin Tuip
Michael Dong
Michele Deo
Mitch Tulloch
Nicolas Blank
Pavel Nagaev
Ragnar Harper
Ricardo Silva
Richard Wakeman
Russ Iuliano
Santhosh Hanumanthappa
Shannal L. Thomas
Steve Bryant
Steve Craig
Todd Walker
Tracey J. Rosenblath

 

 
 

       How did you like this article? Please vote and let us know.          

 

How to create MS Exchange Profiles in MS Outlook with MAPI

 

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

  1. Initialize MAPI;
  2. Get an IProfAdmin interface;
  3. Get a pointer to a Profile table that contains all profiles. Each profile occupies one row.
  4. Now you have a table of profiles and may use HrQueryAllRows() function to filter row(s) that you need.
  5. 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


Disclaimer: Your use of the information contained in these pages is at your sole risk. All information on these pages is provided "as is", without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by Pro Exchange. OutlookExchange.Com and Pro Exchange shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.

© Copyright Pro Exchange, Inc., 2006