Introduction
As REST interfaces take over the world, Microsoft is taking less and less care of it's 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. - Supports 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 enums and forward declarations to two separate files first, then create the header file and #include
both of these files.
e.g:
idl2cpp XML.idl /enums > xml_enums.h
idl2cpp XML.idl /fwd_decls > xml_fwd_decls.h
idl2cpp XML.idl /h > xml.h
Finally you can output the cpp file:
idl2cpp XML.idl /cpp > xml.cpp
Output
Here is a flavour of the output you can expect:
xml_enums.h
#pragma once
namespace MSXML2
{
enum class _SCHEMACONTENTTYPE
{
SCHEMACONTENTTYPE_EMPTY = 0,
SCHEMACONTENTTYPE_TEXTONLY = 1,
SCHEMACONTENTTYPE_ELEMENTONLY = 2,
SCHEMACONTENTTYPE_MIXED = 3
};
...
xml_fwd_decls.h
#pragma once
namespace MSXML2
{
struct IXMLDOMNode;
struct IXMLDOMNodeList;
struct IXMLDOMNamedNodeMap;
...
xml.h
#pragma once
#pragma warning(push)
#pragma warning(disable:4275)
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;
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);
...
xml.cpp
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;
}
...
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
helpstring
s. - 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
.
I started programming in 1983 using Sinclair BASIC, then moved on to Z80 machine code and assembler. In 1988 I programmed 68000 assembler on the ATARI ST and it was 1990 when I started my degree in Computing Systems where I learnt Pascal, C and C++ as well as various academic programming languages (ML, LISP etc.)
I have been developing commercial software for Windows using C++ since 1994.