My blog
My LinkedIn Profile

BOOKS i'm reading

Napoleon Hill Keys to Success: The 17 Principles of Personal Achievement, Napoleon Hill, ISBN: 978-0452272811
The 4-Hour Workweek: Escape 9-5, Live Anywhere, and Join the New Rich (Expanded and Updated), Timothy Ferriss, ISBN: 978-0307465351
The Fountainhead, Ayn Rand, ISBN: 0452273331

Automate Microsoft Outlook from C++

Olivier Langlois, IT superstar coach, Dominate LinkedIn Formula author
by Olivier Langlois


Accessing the Outlook COM interfaces from C++ (Part 1)

The first step to automate Outlook is to define an interface. Here is the one I have come with:

// Forward declaration
class COutlookStubImpl;

class CRecipient
    explicit CRecipient(LPCTSTR email, LPCTSTR firstName = NULL, LPCTSTR lastName = NULL);
    std::string Format() const;
    std::string m_email;
    std::string m_firstName;
    std::string m_lastName;

class COutlookStub
    enum StubMethod
    COutlookStub( StubMethod eMethod = COM );

    bool Init();
    void SetDeleteAfterSubmit( bool val ) { m_bDeleteAfterSubmit = val; }
    bool Send(LPCTSTR Subject, LPCTSTR To, LPCTSTR Body);
    bool Send(LPCTSTR Subject, const CRecipient &recipient, LPCTSTR Body)
    { return Send(Subject,recipient.Format().c_str(),Body); }

     * This class is using the envellope/letter pattern to really
     * encapsulate the method by which it contacts Outlook.
    COutlookStubImpl *m_pOutlookStubImpl;
    bool              m_bDeleteAfterSubmit;

First, the defined interface is very simple and could easilly be extended. For the purpose of this article, defining a Send() method is enough. I have used the envellope/letter design pattern to really encapsulate the COM stuff from the user application. This step is not essential but I'm planning ahead since there exist more methods to access Outlook than with COM and as you will see later, there is a drawback by using Outlook COM interface. By using the envellope/letter design pattern, it will allow me later to add and use other methods just by adding an item to the StubMethod enumeration. The other point of interest is that I moved out the initialization code from the constructor and placed it in Init(). The reason for doing so is that the initialization code could fail for many reasons such as if Outlook is not installed and I do not like to place code that might fail in constructors and rely on exceptions to trap the error. Here is how the sample program use Init() in the application InitInstance() function:

// Check first if a connection to Outlook is possible.
if( dlg.m_OutlookStub.Init() )
    INT_PTR nResponse = dlg.DoModal();
    MessageBox(NULL,_T("Connecting to Outlook 2003 has failed"),
               _T("Outlook Automation with C++"),MB_OK|MB_ICONERROR);

The next step is to create an OutlookStubImpl class for COM. The class name for this stub implementation is CCOMOutlookStubImpl. First, in the CCOMOutlookStubImpl module, I defined the following global object:

struct InitOle {
    InitOle()  { m_bMustUninitialize = OLI_SUCCEEDED(::CoInitialize(NULL)); }
    ~InitOle() { if(m_bMustUninitialize) ::CoUninitialize();   }
    bool m_bMustUninitialize;
} _init_InitOle_;

CoInitialize() call is mandatory for a program to use COM. I am not sure if the MFC initialization code is already making this function call but there is no harm for calling it more than once as long there is the same number of calls to CoUninitialize() than there is successful calls to CoInitialize(). Next, we will start using the information that has been gathered in the preliminary section. In the CCOMOutlookStubImpl header file, the following preprocessor directives are added:

 * The Microsoft Office 11 Object type library is required as it is referenced by the
 * Outlook type library.
#import "libid:2DF8D04C-5BFA-101B-BDE5-00AA0044DE52" rename("EOF","EOFile")

// This statement is needed otherwise the generated header file for msout.olb will not
// compile.
using namespace Office;
 * Please consult the Outlook Type library from Tools/OLE/COM object viewer
 * to know its exact location as it might vary from one computer to the other.
 * Note, it is important to place the import statement first as there is a conflict with
 * a macro named CopyFile defined in winbase.h
#import "libid:00062FFF-0000-0000-C000-000000000046" rename("EOF","EOFile")

It tells to compiler to fetch the specified COM type libraries and to generate C++ header files that can be used from our program. The libid keyword specify to the compiler to search in the registry to find the location of the type library. You can directly specify the path but by doing so, it makes the code source much less portable since the type libraries location can vary from one system to the other. I found this tip in an article from Heath Stewart.

Page 1 2 3 4

Home :: Fractals :: Tutorials :: Books :: My blog :: My LinkedIn Profile :: Contact