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:
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
namespace MSXML2
{
enum class _SCHEMACONTENTTYPE
{
SCHEMACONTENTTYPE_EMPTY = 0,
SCHEMACONTENTTYPE_TEXTONLY = 1,
SCHEMACONTENTTYPE_ELEMENTONLY = 2,
SCHEMACONTENTTYPE_MIXED = 3
};
...
msxml2_fwd_decls.h
#pragma once
namespace MSXML2
{
struct IXMLDOMNode;
struct IXMLDOMNodeList;
struct IXMLDOMNamedNodeMap;
...
msxml2.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);
...
msxml2.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;
}
...
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
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
- 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 #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.