Getting around general COM nastiness

If you’re working with COM objects and would like to get around the general COM nastiness of having to separate out the definition and initialization of COM variables you can use the following wrapper:

/* std::exception needs to copy the string otherwise */
static const char *const com_wrap_exception_msg = "COM call failed"; 

struct com_wrap_exception : std::exception {
       com_wrap_exception() throw()
           : std::exception( com_wrap_exception_msg, 1 ) {}

template <typename T>
struct com_wrap_ret_t { typedef CComPtr<T> type; }; 

template <>
struct com_wrap_ret_t <BSTR> { typedef CComBSTR type; };

template <typename T, typename P, typename R>
inline typename com_wrap_ret_t<P>::type 
wrap(typename com_wrap_ret_t<T>::type e, R (__stdcall T::*Mfn)(P*)) {
    typename com_wrap_ret_t<P>::type s;
    if (FAILED((e->*Mfn)( &s )))
        throw com_wrap_exception();
    return s;

A sample call to this wrapper would be:

CComPtr<_MailItem> email;
// do something to initialize email 
CComBSTR s = wrap( email, &_MailItem::get_Body ); /* no longer need to do: CComBSTR s; email->get_Body( &s ); */

Note that the code correctly handles both CComBSTR and CComPtr. Extending this to CAdapt or another special type (such as COleDateTime) is trivial.

Also, in a release build I’d probably remove the exception (to get NRVO) and replace it by some other equivalent error handler.



About this entry