Home
Fractals
Tutorials
Books
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

Contents





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

So now, we are ready for the actual meat:

bool CCOMOutlookStubImpl::Init()
{
    bool res = true;
    HRESULT hr;
    try
    {
        hr = m_OutlookAppPtr.CreateInstance( __uuidof(Outlook::Application) );
        if(OLI_FAILED(hr))
        {
            res = false;
        }
    }
    catch ( _com_error &ce )
    {
      COMMERROR(ce.Error());
      res = false;
    }
    return res;
}

bool CCOMOutlookStubImpl::Send(LPCTSTR Subject, LPCTSTR To, LPCTSTR Body, bool bDeleteAfterSubmit)
{
    bool res = true;
    HRESULT hr;
    try
    {
        IDispatchPtr IMailItemDispatchPtr;
        IMailItemDispatchPtr = m_OutlookAppPtr->CreateItem(Outlook::olMailItem);

        // Query the MailItem interface
        Outlook::_MailItemPtr IMailItemPtr;
        Outlook::_MailItem *IMailItem = NULL;
        hr = IMailItemDispatchPtr.QueryInterface(__uuidof(Outlook::_MailItem),&IMailItem);
        if(OLI_FAILED(hr))
        {
            return false;
        }
        IMailItemPtr.Attach(IMailItem);

        IMailItemPtr->Subject = Subject;
        IMailItemPtr->To      = To;
        IMailItemPtr->Body    = Body;

        // It is very important to use VARIANT_TRUE as VB represents TRUE as -1
        // vs C++ where TRUE is 1.
        IMailItemPtr->DeleteAfterSubmit = (bDeleteAfterSubmit?VARIANT_TRUE:VARIANT_FALSE);
        hr = IMailItemPtr->Send();
        if(OLI_FAILED(hr))
        {
            res = false;
        }
    }
    catch ( _com_error &ce )
    {
      if( HRESULT_CODE(ce.Error()) == 0x4004 )
      {
          MessageBox(NULL,_T("You did not let this program to send the e-mail"),
                     _T("COMOutlookStub"),MB_OK|MB_ICONINFORMATION);
      }
      else
      {
          // Unexpected error.
          COMMERROR(ce.Error());
          DiagCOM diag;
          diag.display( ce, __FILE__, __LINE__ );
      }
      res = false;
    }
    return res;
}

First, the compiler COM support is a blessing by making COM development with C++ almost as easy as in VB. It encapsulates each COM interface pointer in a smart pointer, _com_ptr_t. It takes care of automatically to call AddRef() and Release() for you. Also, it makes the code clearer as instead of polluting your code with repetive HRESULT error checking code everytime you make a COM call, the smart pointer will throw an exception, _com_error. It is a very nice feature but you will need to not forget to wrap your COM code around try/catch blocks. Finally, another nicety of the generated COM interfaces definition header files using the compiler smart pointers is that you can access the COM interfaces properties as if they were simple class data members. The compiler helper classes, _bstr_t and _variant_t automatically convert C strings and other built in types into the appropriate COM type. You do not want to start converting manually your C strings in Basic strings. Trust me on that, you don't :-) The following code snippet takes advantage of this cool feature:

        IMailItemPtr->Subject = Subject;
        IMailItemPtr->To      = To;
        IMailItemPtr->Body    = Body;

        // It is very important to use VARIANT_TRUE as VB represents TRUE as -1
        // vs C++ where TRUE is 1.
        IMailItemPtr->DeleteAfterSubmit = (bDeleteAfterSubmit?VARIANT_TRUE:VARIANT_FALSE);

The only exception is when a property has the type VARIANT_BOOL. This is the only potential pitfall that I have encountered while developing this code. As the comment states, if you set this property with TRUE or true, the result is undefined. I have not try it to see what happens but what I present is the correct way to set a VARIANT_BOOL property. Now that the small COM details are out of the way, the procedure is very simple:

  1. Create a new Outlook Application COM object
  2. Call CreateItem() on the Application object to get a new MailItem object interface
  3. Set various MailItem properties
  4. Call MailItem::Send()

I think that the only remaining COM intricacy is:

        IDispatchPtr IMailItemDispatchPtr;
        IMailItemDispatchPtr = m_OutlookAppPtr->CreateItem(Outlook::olMailItem);

        // Query the MailItem interface
        Outlook::_MailItemPtr IMailItemPtr;
        Outlook::_MailItem *IMailItem = NULL;
        hr = IMailItemDispatchPtr.QueryInterface(__uuidof(Outlook::_MailItem),&IMailItem);

MailItem has a dual interface. An object having a dual interface is an object offering an interface with a vtable and also having a IDispatch interface to access the same functionnality. Application.CreateItem() returns the latter. It is technically possible to manipulate directly an object through its IDispatch interface in C++ but this is not something very funny to do so as soon as we get the MailItem IDispatch interface, we query immediately the vtable interface from it to make our life easier. For the curious, IDispatch is mostly for interpreted languages such as VB to permit late binding. The only time where you will have to work with IDispatch is if you are developing COM objects for VB users and you have to implement an IDispatch interface yourself!



Page 1 2 3 4

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