Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

An MFC COM Wrapper Code Generator Using COleDispatchDriver

5.00/5 (7 votes)
19 Jun 2022CPOL3 min read 6.3K   278  
IDispatch C++ Code Generation Done Right
This article discusses the development of a code generator aimed at improving the functionality of COleDispatchDriver code generator, accompanied by instructions for usage and sample output.

Introduction

As REST interfaces take over the world, Microsoft is taking less and less care of its native code tools. The latest COleDispatchDriver code generator is a disaster, producing code that doesn't even compile. Luckily, I had already written my own generator to address historical shortcomings for work and this was the final shove I needed to finish off the open source version I had embarked on in 2018.

Background

This code generator aims to:

  • Parse IDL generated from OLE Viewer flawlessly
  • Automatically use the library name as a namespace
  • Output the helpstring as a comment for the library
  • Generate enum declarations
  • Output enum names (not long) on functions
  • Output VARIANT when it does not recognise a type instead of a comment saying the function cannot be supported
  • Output real types instead of IDispatch for everything
  • Support COM events code generation

Using the Code

First of all, you will need access to oleview.exe. You can find this under the Windows SDK. For example, on my machine, the EXE is under C:\Program Files (x86)\Windows Kits\10\bin\10.0.20348.0\\x64\oleview.exe. Note that you may need to register iviewers.dll using the command regsvr32 iviewers.dll as Administrator.

Now pick an interface in OLE Viewer (for example, Microsoft XML) and view the IDL:

Image 1

Image 2

Use Ctrl-A to select all the code and then paste it into a text file and save that with a .idl extension.

Recursively repeat this process for any type libraries that are imported via the importlib command in the IDL (in this case, it is stdole2.tlb).

You can now run the idl2cpp exe on the .idl file in question.

For example:

BAT
idl2cpp XML.idl

When run like this with no switches, you should just receive a message that the file parsed OK. To output some code, you have a choice of:

  • /enums
  • /events_h
  • /events_cpp
  • /fwd_decls
  • /h
  • /cpp

Note that these switches are mutually exclusive.

It is best to output the enums and forward declarations to two separate files first, then create the header file and #include both of these files.

For example:

BAT
idl2cpp XML.idl /enums > msxml2_enums.h

idl2cpp XML.idl /fwd_decls > msxml2_fwd_decls.h

idl2cpp XML.idl /h > msxml2.h

Finally, you can output the cpp file:

BAT
idl2cpp XML.idl /cpp > msxml2.cpp

Output

Here is a flavour of the output you can expect:

msxml2_enums.h
C++
#pragma once

// GENERATED CODE by idl2cpp 2022-06-19 13:26:24
namespace MSXML2
{
// Schema Object Model Content Types
enum class _SCHEMACONTENTTYPE
{
        SCHEMACONTENTTYPE_EMPTY = 0,
        SCHEMACONTENTTYPE_TEXTONLY = 1,
        SCHEMACONTENTTYPE_ELEMENTONLY = 2,
        SCHEMACONTENTTYPE_MIXED = 3
};

...
msxml2_fwd_decls.h
C++
#pragma once

// GENERATED CODE by idl2cpp 2022-06-19 13:27:25
namespace MSXML2
{
        struct IXMLDOMNode;
        struct IXMLDOMNodeList;
        struct IXMLDOMNamedNodeMap;

...
msxml2.h
C++
#pragma once

// GENERATED CODE by idl2cpp 2022-06-19 13:29:27

// http://support.microsoft.com/kb/134980
#pragma warning(push)
#pragma warning(disable:4275)

// Microsoft XML, v6.0
namespace MSXML2
{
using DOMNodeType = tagDOMNodeType;
using SCHEMACONTENTTYPE = _SCHEMACONTENTTYPE;
using SCHEMADERIVATIONMETHOD = _SCHEMADERIVATIONMETHOD;
using SCHEMAPROCESSCONTENTS = _SCHEMAPROCESSCONTENTS;
using SCHEMATYPEVARIETY = _SCHEMATYPEVARIETY;
using SCHEMAUSE = _SCHEMAUSE;
using SCHEMAWHITESPACE = _SCHEMAWHITESPACE;
using SERVERXMLHTTP_OPTION = _SERVERXMLHTTP_OPTION;
using SOMITEMTYPE = _SOMITEMTYPE;
using SXH_PROXY_SETTING = _SXH_PROXY_SETTING;
using SXH_SERVER_CERT_OPTION = _SXH_SERVER_CERT_OPTION;
using XHR_PROPERTY = _XHR_PROPERTY;

// Core DOM node interface
struct AFX_EXT_CLASS IXMLDOMNode : COleDispatchDriver
{
        IXMLDOMNode() {}
        IXMLDOMNode(LPDISPATCH pDispatch) :
                COleDispatchDriver(pDispatch) {}
        IXMLDOMNode(const IXMLDOMNode& dispatchSrc) :
                COleDispatchDriver(dispatchSrc) {}

        CString GetnodeName();
        VARIANT GetnodeValue();
        void SetnodeValue(const VARIANT& newValue);

...
msxml2.cpp
C++
// GENERATED CODE by idl2cpp 2022-06-19 13:30:45
namespace MSXML2
{
CString IXMLDOMNode::GetnodeName()
{
        CString result{};

        InvokeHelper(0x2, DISPATCH_PROPERTYGET, VT_BSTR, &result, nullptr);
        return result;
}

VARIANT IXMLDOMNode::GetnodeValue()
{
        VARIANT result{};

        InvokeHelper(0x3, DISPATCH_PROPERTYGET, VT_VARIANT, &result, nullptr);
        return result;
}

...

Note that the header file generated automatically has #includes for msxml2_enums.h and msxml2_fwd.h.

The source file generated will also automatically have a #include for msxml2.h.

Currently the generator for /events_cpp requires that a #include for the corresponding header be added manually.

History

  • 19/06/2022: Created
  • 19/06/2022: Added missing files to zip file
  • 19/06/2022: Stop converting int to long for return values
  • 19/06/2022: Added switches /events_h and /events_cpp
  • 19/06/2022: Output nullptr instead of 0 as default for pointers
  • 24/06/2022: Various bug fixes
  • 02/07/2022: Added support for function helpstrings
  • 02/07/2022: Treat CURRENCY and VARIANT consistently. Only use const when an in only parameter
  • 04/07/2022: int64 and uint64 should not create a comment
  • 04/07/2022: Default optional params to 0/nullptr where a value is not specified
  • 19/10/2022: Fixed broken link in output_header.cpp
  • 21/01/2023: Updated to use the latest parsertl
  • 31/01/2023: Updated to use the latest parsertl
  • 11/09/2023: Updated lexertl, parsertl and Makefile. Fixed some g++ warnings
  • 02/12/2023: Now outputting real types where possible rather than IDispatch* all the time
  • 05/12/2023: Set operator LPDISPATCH to delete and output #includes automatically
  • 10/12/2023: Improved comments for events source generation. Also made changes suggested by SonarLint
  • 22/12/2023: Added /name switch so that a single interface can be output
  • 26/12/2023: Added missing newline to events_template.cpp
  • 15/02/2023: Updated to use lexertl17 and parsertl17
  • 06/04/2024: Added slightly more explanation.
  • 07/04/2024: Now handling coclass parameters correctly.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)