VOLE - A Neat C++ COM/Automation Driver

VOLE is an open-source C++ library that dramatically simplifies the business of driving COM servers (via the IDispatch interface) from C++.

VOLE is highly robust.

VOLE is very flexible, taking and returning normal C++ types, such as int, double, std::string, std::wstring, and so on.

VOLE is 100% header only.

VOLE is compiler-independent, and has been verified to work with the following compilers:

VOLE is dependent only on the STLSoft libraries (version 1.9.90, or later; available from http://stlsoft.org/), which are themselves 100% header-only.

VOLE is one of the exemplar projects for Matthew Wilson's new book, "Breaking Up The Monolith" (http://breakingupthemonolith.com/), which is currently in preparation, and will be published by Addison-Wesley in 2010.

VOLE employs the Type Tunnel and Handle::Ref+Wrapper patterns, both of which are described in the early chapters of the book. These highly flexible mechanisms afford great expressiveness and genericity, without sacrificing robustness, performance or incurring unnecessary coupling.

VOLE 0.6.5 is now available.

Two articles on VOLE - "A Simple XML Validator, using VOLE" and "Driving Microsoft Word, using VOLE" - are now available on Code Project.

Code examples:

1. This code creates an instance of pantheios.COM.LoggerManager, then obtains from it an instance of the Console logger, and then logs a simple message to it, obtains a couple of properties from it, and then logs another message.

  using vole::object;

  object loggerManager = object::create("pantheios.COM.LoggerManager");
  object logger        = loggerManager.invoke_method<object>(L"GetLogger", L"Console", L"VOLE pantheios.COM test");

  logger.invoke_method<void>(L"Log", 3, L"The answer is: ", 43);

  std::string processId  = logger.get_property<std::string>(L"ProcessIdentity");
  long        backEndId  = logger.get_property<long>(L"BackEndId");

  logger.invoke_method<void>(L"Log", 4, "abc(", L"DEF", "): ", 2319, " - ", 19.19, L" - ", std::string("yada!").c_str());

2. This code drives use the openrj.COM server to open a Record-JAR database file and enumerate its contents:

  #include <vole/vole.hpp>

  #include <comstl/error/errorinfo_desc.hpp>
  #include <comstl/util/initialisers.hpp>

  int main(int argc, char *argv[])
  {
   using std::cout;
   using std::cerr;
   using std::endl;

   try
   {
    comstl::com_init init;

    if(argc < 2)
    {
     cout << "USAGE: openrj.com.text.exe <db-file>" << endl;
    }
    else
    {
     using vole::object;
     using vole::collection;

     // Create the COM server, via its ProgID "openrj.COM.database"

     char const *dbFile  = argv[1];
     collection db       = object::create("openrj.COM.database");

     // Open the given file

     db.invoke_method<void>(L"OpenFile", dbFile);

     // Use the collection::Count property to get the number of records
     // in the database file, and access the File property via
     // object::get_property<>()

     long        numRecords = db.Count;
     std::string fileName   = db.get_property<std::string>(L"File");

     cout << "Database in file '" << fileName << "' contains " << numRecords << " record(s)" << endl;

     // Enumerate through the records of the database, via the STL-compatible
     // collection::iterator class, accessed through the
     // collection::begin() and collection::end() methods

     { for(collection::iterator b = db.begin(); b != db.end(); ++b)
     {
      // "Convert" the returned VARIANT into a collection

      collection  record(*b);
      long        numFields = record.Count;

      cout << "  Record has " << numFields << " field(s)" << endl;

      // Enumerate through the fields of the record, via the STL-compatible
      // collection::iterator class, accessed through the
      // collection::begin() and collection::end() methods

      { for(collection::iterator b2 = record.begin(); b2 != record.end(); ++b2)
      {
       // "Convert" the returned VARIANT into an object

       object       field(*b2);

       // Access the Name and Value properties of the
       // field, via object::get_property<>()

       std::string  name  = field.get_property<std::string>(L"Name");
       std::string  value = field.get_property<std::string>(L"Value");

       cout << "    " << name << "=" << value << endl;
      }}
     }}
    }

   }
   catch(vole::vole_exception &x)
   {
    cerr << x.what() << ": " << comstl::errorinfo_desc() << endl;
   }
   catch(std::exception &x)
   {
    cerr << x.what() << endl;
   }

   return 0;
  }

3. This code creates use a commercial COM Component of Synesis Software to enumerate the NT Services on the host system:

  using vole::object;
  using std::endl;
  using std::wcout;

  object ServiceManager = object::create("SynesisSoftware.WindowsNt.ServiceManager");
  long   timeout        = ServiceManager.get_property<long>(L"Timeout");
  object Services       = ServiceManager.get_property<object>(L"Services");
  long   numServices    = Services.get_property<long>(L"Count");

  { for(long i = 0; i < numServices; ++i)
  {
    object       Service      = Services.get_property<object>(L"Item", i);
    comstl::bstr ServiceName  = Service.get_property<comstl::bstr>(L"ServiceName");
    comstl::bstr DisplayName  = Service.get_property<comstl::bstr>(L"DisplayName");

    wcout << L"[" << ServiceName << L"] " << DisplayName << endl << endl;
  }}