An MFC COM Wrapper Code Generator Using COleDispatchDriver





5.00/5 (7 votes)
IDispatch C++ Code Generation Done Right
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 (notlong
) 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:
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:
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 enum
s and forward declarations to two separate files first, then create the header file and #include
both of these files.
For example:
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:
idl2cpp XML.idl /cpp > msxml2.cpp
Output
Here is a flavour of the output you can expect:
msxml2_enums.h
#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
#pragma once
// GENERATED CODE by idl2cpp 2022-06-19 13:27:25
namespace MSXML2
{
struct IXMLDOMNode;
struct IXMLDOMNodeList;
struct IXMLDOMNamedNodeMap;
...
msxml2.h
#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
// 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 #include
s 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
tolong
for return values - 19/06/2022: Added switches
/events_h
and/events_cpp
- 19/06/2022: Output
nullptr
instead of0
as default for pointers - 24/06/2022: Various bug fixes
- 02/07/2022: Added support for function
helpstring
s - 02/07/2022: Treat
CURRENCY
andVARIANT
consistently. Only useconst
when anin
only parameter - 04/07/2022:
int64
anduint64
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
andMakefile
. 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
todelete
and output#include
s 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.