Microsoft Visual C++ 5.0 Compiler Native COM Support

Presented by: Philip Lucido
Software Design Engineer
Microsoft Visual C++ Compiler Development
Microsoft Corporation

1. Introduction

The Microsoft® Visual C++® compiler in Visual C++ 5.0 has been extended to include native support for COM programming, primarily on the client side. The major new feature is the ability to directly read type libraries, translating the contents into C++ source that is included in the compilation source. In addition, there are language extensions for COM support. Finally, we’ve added library and runtime support needed by the compiler typelib translation.

The intent is not to radically extend the C++ language. Instead, the compiler reads a type library and converts it into a header file that captures the typelib’s contents as equivalent C++ classes, mostly describing the COM interfaces described therein. This converted header file uses normal C++ syntax, with a few modifications to expose COM semantics.

Except as noted all changes are available only for the C++ compiler. Since COM is, in effect, based on vtables, it is most natural to express a typelib’s contents as C++ abstract base classes, with non-virtual inline member functions to provide wrappers for easier access. This can be done with almost no language extensions. Where reasonable, the actual language extensions are available in both C and C++.

Note: This paper explains what has been added to the Visual C++ 5.0 compiler. It does not explain how to use the new features. Example code exercising these features can be found in the samples.

2. Reading Type Libraries

2.1 The #import Preprocessor Directive

A new preprocessor directive, #import, is used to read a type library. It has the syntax

#import "filename" [attributes]
#import <filename> [attributes]

where the filename is either a typelib, an EXE or DLL containing a typelib resource (e.g., an ActiveX control OCX), a compound document holding a typelib, or any other file which is understood by the LoadTypeLib API. The compiler searches for the named file first, if using the quoted name form, in the directories of the parent files (same as for #include). Both forms then search along the PATH environment variable path list, then along the LIB list, then along the paths specified by the /I compiler option, then the INCLUDE list. The search logic duplicates that of #include, except for looking along the PATH and LIB environment variable lists. #import is only available in the C++ compiler.

#import works by creating two header files that reconstitute the typelib contents as C++ source code. The primary header file is given the base filename of the typelib, with an extension of .tlh. The secondary header file is given the same basename, with an extension of .tli. Both headers are placed in the output directory, as named in the -Fo option. The primary header is similar to that produced by MIDL, but with additional compiler-generated code and data. The secondary header holds the actual implementations for compiler-generated member functions, and is #included by the primary header. After the headers have been created, they are read and compiled as if the primary header was named in a #include directive.

#import can optionally include one or more attributes, which are directives to the compiler to modify the contents of the typelib header files. A description of the syntax and meaning of these attributes is found in section 0.

This process of converting a typelib to C++ source code solves a thorny problem by giving the compiler a place to issue an error message if something goes wrong, such as a name collision. Without source, it would be difficult pointing a programmer at the actual problem encountered somewhere within a binary-format typelib.

There are a few optimizations here. The headers, when created, are given the same timestamp as the typelib. When a #import is processed, the compiler first checks if the headers exist and are up-to-date. If yes, then they do not need to be recreated. Also, #import participates in MinRebuild and can be placed in a PCH, which will avoid a lot of unnecessary work (the created headers can be tens of thousands of lines long for some typelibs). The compiler also delays initializing the OLE subsystem until the first #import that really needs to create new headers is encountered.

2.2 The typelib Headers

Example versions of the typelib headers can be found in section 0.

The primary typelib header consists of these sections:

The heading boilerplate consists of comments, a #include of a standard include header, comdef.h, which defines some standard macros used within the header, and other miscellaneous setup. The footer boilerplate just does a #pragma pop.

The other primary header items are all enclosed within a namespace that is identified by the name defined in the library statement in the original ODL. This is done to avoid name collision problems. Names from the typelib header either have to be used fully qualified with the namespace name, or a statement like using namespace MyLib; must appear after doing a #import. The namespace can be suppressed by using the no_namespace import attribute, or renamed via the rename_namespace or rename attributes.

2.3 Typeinfo Definitions in the typelib Header

The bulk of the typelib header consists of class definitions and other items exposing the individual typeinfo items returned via ITypeLib::GetTypeInfo. This is preceded by forward reference declarations, e.g., struct IMyInterface, typedefs for any TKIND_ALIAS items, and smart pointer declarations (see section 0). These must be exposed before the full typeinfo definitions to avoid out-of-order compile errors.

After the forward declarations and typedefs, each typeinfo from the typelib is echoed to the header in a form dependent on the typeinfo kind.

2.3.1 TKIND_MODULE - module declarations

In this compiler release, a module definition is ignored.

2.3.2 TKIND_ENUM, TKIND_RECORD, TKIND_UNION

These three kinds of typeinfo items are trivially exposed, as normal enum, C-style structure or union definitions.

2.3.3 TKIND_INTERFACE - COM interfaces

A COM interface is exposed as an abstract base class, with some of the pure virtual member functions wrapped in nonvirtual inline members that primarily help hide checking the HRESULT error code. The following example code will help explain. First, here’s the ODL script for an interface:

[ uuid(eec57af0-d8e9-11cf-82c6-00aa003d90f3), odl ]
interface IMyInterface : IUnknown
{
    [ propget ] HRESULT Sound( [out, retval] long *freq );
    [ propput ] HRESULT Sound( [in] long freq );
    HRESULT Method1( [in] long input );
    HRESULT Method2( [out, retval] long *output );
    HRESULT RetBSTR( [out, retval] BSTR *pbstr);
    HRESULT VarTest( [in] VARIANT var, [out, retval] VARIANT *pvar);
    HRESULT PtrTest( [out, retval] IMyInterface ** pinterface);
    long Query( [in] int index );
};

This is translated into a definition of struct IMyInterface in the primary header, and definitions of the inline member functions in the secondary header. First, here is the definition of IMyInterface in the primary header:

struct __declspec(uuid("eec57af0-d8e9-11cf-82c6-00aa003d90f3"))
IMyInterface : IUnknown
{
    //
    // Property data
    //
    __declspec(property(get=GetSound,put=PutSound))
    long Sound;
    //
    // Wrapper methods for error-handling
    //
    long GetSound ( );
    void PutSound (
        long freq );
    HRESULT Method1 (
        long input );
    long Method2 ( );
    _bstr_t RetBSTR ( );
    _variant_t VarTest (
        const _variant_t & var );
    IMyInterfacePtr PtrTest ( );
    //
    // Raw methods provided by interface
    //
    virtual HRESULT __stdcall get_Sound (
        long * freq ) = 0;
    virtual HRESULT __stdcall put_Sound (
        long freq ) = 0;
    virtual HRESULT __stdcall raw_Method1 (
        long input ) = 0;
    virtual HRESULT __stdcall raw_Method2 (
        long * output ) = 0;
    virtual HRESULT __stdcall raw_RetBSTR (
        BSTR * pbstr ) = 0;
    virtual HRESULT __stdcall raw_VarTest (
        VARIANT var,
        VARIANT * pvar ) = 0;
    virtual HRESULT __stdcall raw_PtrTest (
        struct IMyInterface * * pinterface ) = 0;
    virtual long __stdcall Query (
        int index ) = 0;
};

Here are the member function implementations from the secondary header:

//
// interface IMyInterface wrapper method implementations
//
inline long IMyInterface::GetSound ( ) {
    long _result;
    HRESULT _hr = get_Sound(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _result;
}
inline void IMyInterface::PutSound ( long freq ) {
    HRESULT _hr = put_Sound(freq);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
}
inline HRESULT IMyInterface::Method1 ( long input ) {
    HRESULT _hr = raw_Method1(input);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _hr;
}
inline long IMyInterface::Method2 ( ) {
    long _result;
    HRESULT _hr = raw_Method2(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _result;
}
inline _bstr_t IMyInterface::RetBSTR ( ) {
    BSTR _result;
    HRESULT _hr = raw_RetBSTR(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _bstr_t(_result, false);
}
inline _variant_t IMyInterface::VarTest ( const _variant_t & var ) {
    VARIANT _result;
    VariantInit(&_result);
    HRESULT _hr = raw_VarTest(var, &_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _variant_t(_result, false);
}
inline IMyInterfacePtr IMyInterface::PtrTest ( ) {
    struct IMyInterface * _result;
    HRESULT _hr = raw_PtrTest(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return IMyInterfacePtr(_result, false);
}

First off, this exposes the raw interface. The interface is declared as a struct, suitably inherited as per the typelib. The interface methods appear as pure virtuals with signatures matching those in the typelib. The raw methods appear last within the struct, so the easier error-handling alternatives are more prominent.

Next, the IID is exposed. For old-style code expecting to use IID_IMyInterface, this symbol can optionally be defined in a later part of the primary header (see the named_guids attribute, section 0). Alternately, a new intrinsic, __uuidof, is introduced. When applied to an interface, it returns the GUID for that interface. This works via __declspec(uuid(…)), which is used to attach a GUID to a struct or class. This is discussed further in section 0.

The next items to notice are the wrapper functions for any methods that return an HRESULT. These wrappers are non-virtual inline functions that call the virtual method, then check the return code. If a failure code is returned, then a runtime error handler, _com_issue_errorex, is called. The actual behavior of this error handling is discussed in section 0. For now, I’ll examine the various wrapper functions.

If an HRESULT-returning method has an argument with out and retval attributes, then a wrapper is created with the same function name, but with the retval argument removed and the return type changed to a dereference of the retval pointer type. See Method2 for an example.

If an HRESULT-returning non-property method has no retval argument, as for Method1, then the wrapper will return an HRESULT. The wrapper will still throw an error in case of a failure code, but this allows a user to check for non-S_OK success codes in the most common case. This and the similar case for dispinterfaces are the only places where HRESULT success codes are visible when using the error-handling wrappers.

Method and property names are derived from the raw names found in the typelib by adding a prefix. The non-virtual wrapper functions for non-property methods are given the actual typelib method name, unprefixed. The raw virtual functions for non-property methods are prefixed by raw_. Respectively, Get, Put, and PutRef prefix the error-handling wrapper functions for propget, propput, and propputref methods. Raw virtual functions for propget, propput, and propputref methods are prefixed by get_, put_, and _putref. These prefixes can be modified by using the raw_interfaces_only, raw_method_prefix, high_method_prefix, raw_property_prefixes, and high_property_prefixes attributes.

Methods RetBSTR, VarTest, and PtrTest illustrate the wrapping of native COM data types in the error-handling wrappers. A BSTR is passed and returned as a _bstr_t, a VARIANT is passed and returned as a _variant_t, and a COM interface pointer is returned as a specialization of template class _com_ptr_t. These three classes, made available via comdef.h and its associated headers, handle the appropriate allocation and release of resources for these types, freeing the user from the details. More explanation is available in sections 0 and 0.

The actual wrapper implementations are all found in the secondary header. This allows sophisticated users to avoid compiling all the implementations for every object, via the no_implementation attribute.

Finally, the __declspec(property()) declaration of member data Sound is there to allow data-oriented access to properties, e.g., pMyInterface->Sound += 5; instead of pMyInterface->PutSound(pMyInterface->GetSound()+5);. This language extension is described in section 0.

2.3.4 TKIND_DISPATCH - dispatch interfaces

A dispatch interface is exposed as an abstract base class derived from IDispatch, with non-virtual member functions wrapping the IDispatch::Invoke calls. Again, an explanation by example, with the ODL script first:

[ uuid(eec57af1-d8e9-11cf-82c6-00aa003d90f3) ]
dispinterface IMyDispInterface
{
properties:
    [ id(1) ] long Sound;
methods:
    [ id(2) ] void Method1( [in] long input );
    [ id(3) ] long Method2( );
    [ id(4) ] long Query( [in] int index );
    [ id(5) ] BSTR RetBSTR( );
    [ id(6) ] VARIANT VarTest( [in] VARIANT var );
    [ id(7) ] IMyDispInterface* PtrTest( );
    [ propget, id(8) ] long Channel( long index );
    [ propput, id(8) ] void Channel( long index, long value );
};

The following definition will be injected into the primary typelib header:

struct __declspec(uuid("eec57af1-d8e9-11cf-82c6-00aa003d90f3"))
IMyDispInterface : IDispatch
{
    //
    // Property data
    //
    __declspec(property(get=GetChannel,put=PutChannel))
    long Channel[];
    __declspec(property(get=GetSound,put=PutSound))
    long Sound;
    //
    // Wrapper methods for error-handling
    //
    // Methods:
    HRESULT Method1 (
        long input );
    long Method2 ( );
    long Query (
        int index );
    _bstr_t RetBSTR ( );
    _variant_t VarTest (
        const _variant_t & var );
    IMyDispInterfacePtr PtrTest ( );
    long GetChannel (
        long index );
    void PutChannel (
        long index,
        long _arg2 );
    // Properties:
    long GetSound ( );
    void PutSound ( long _val );
};

And here are the wrapper implementations from the secondary header:

//
// dispinterface IMyDispInterface wrapper method implementations
//
inline HRESULT IMyDispInterface::Method1 ( long input ) {
    return _com_dispatch_method(this, 0x2, DISPATCH_METHOD, VT_EMPTY, NULL, 
        L"\x0003", input);
}
inline long IMyDispInterface::Method2 ( ) {
    long _result;
    _com_dispatch_method(this, 0x3, DISPATCH_METHOD, VT_I4, (void*)&_result,
        NULL);
    return _result;
}
inline long IMyDispInterface::Query ( int index ) {
    long _result;
    _com_dispatch_method(this, 0x4, DISPATCH_METHOD, VT_I4, (void*)&_result, 
        L"\x0003", index);
    return _result;
}
inline _bstr_t IMyDispInterface::RetBSTR ( ) {
    BSTR _result;
    _com_dispatch_method(this, 0x5, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
        NULL);
    return _bstr_t(_result, false);
}
inline _variant_t IMyDispInterface::VarTest ( const _variant_t & var ) {
    VARIANT _result;
    _com_dispatch_method(this, 0x6, DISPATCH_METHOD, VT_VARIANT,
        (void*)&_result, L"\x000c", &var);
    return _variant_t(_result, false);
}
inline IMyDispInterfacePtr IMyDispInterface::PtrTest ( ) {
    struct IMyDispInterface * _result;
    _com_dispatch_method(this, 0x7, DISPATCH_METHOD, VT_DISPATCH,
        (void*)&_result, NULL);
    return IMyDispInterfacePtr(_result, false);
}
inline long IMyDispInterface::GetChannel ( long index ) {
    long _result;
    _com_dispatch_method(this, 0x8, DISPATCH_PROPERTYGET, VT_I4,
        (void*)&_result, L"\x0003", index);
    return _result;
}
inline void IMyDispInterface::PutChannel ( long index, long _arg2 ) {
    _com_dispatch_method(this, 0x8, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, 
        L"\x0003\x0003", index, _arg2);
}
inline long IMyDispInterface::GetSound ( ) {
    long _result;
    _com_dispatch_propget(this, 0x1, VT_I4, (void*)&_result);
    return _result;
}
inline void IMyDispInterface::PutSound ( long _val ) {
    _com_dispatch_propput(this, 0x1, VT_I4, _val);
}

This code is similar to that provided by the ClassWizard when importing an OLE typelib. The main difference is the dispinterface is directly exposed as a child of IDispatch, with no per-instance data, instead of being derived from COleDispatchDriver. Accordingly, the helper functions that do all the real work are no longer member functions, and the interface pointer is passed to the helpers as a regular argument.

The runtime helper functions package the incoming arguments as appropriate, call IDispatch::Invoke, then handle any output or error that results. By default, wrappers are generated for high-level access, which process any failure by calling the error handling code described in section 0. The raw_dispinterfaces attribute generates low-level wrappers that call IDispatch::Invoke, but still return an HRESULT on failure.

The other item of interest here is the parameterized property data member Channel. This allows data-oriented array indexing access to properties which take extra arguments, e.g., x = pMyDispInterface->Channel[10]; instead of x = pMyDispInterface->GetChannel(10);. See section 0 for further explanation.

2.3.5 TKIND_COCLASS - COM classes

The typelib headers expose a coclass in order to attach a CLSID and perhaps the default interfaces. See the discussions of __declspec(uuid()) in section 0.

2.4 Configuring the typelib Header Contents With #import Attributes

The #import directive can optionally include one or more import attributes after the filename string. These attributes are used to control the contents of the typelib headers, either by enabling or disabling large sections of the headers, or giving more fine-grained control over individual header elements.

An import attribute is an identifier plus any required arguments. If an attribute takes arguments, they consist of one or more string constants in a comma-separated list inside a single set of parentheses, following the attribute identifier. String concatenation is performed on individual attribute arguments, so multiple attributes can be separated by whitespace or commas. Macro substitution is performed on a #import directive before it is processed, so the import attributes can be specified using #define macros. The backslash-newline sequence may also be used to include additional physical lines in a single #import directive.

As an example, the following code fragment will create typelib headers test.tlh and test.tli without wrapping the contents in a namespace. A message pragma is included which will display the text “importing test.tlh” while compiling. Finally, any instance of the identifier OldName in the typelib will be replaced by NewName in the typelib headers.

#define IMPATTRIBS no_namespace \
    inject_statement("#pragma message(\"importing \" __FILE__)")
#import “test.tlb” IMPATTRIBS \
    rename(“OldName”,”NewName”)

2.4.1 no_namespace

The typelib header contents are normally wrapped within a namespace with the name of the typelib declared in the library statement of the original ODL. If the no_namespace attribute is specified, this namespace wrapper is omitted. To use the namespace wrapper but give it some other name, use the rename_namespace attribute.

2.4.2 rename_namespace(“NewName”)

The rename_namespace attribute is used to give a specific name to the namespace that wraps the contents of the typelib headers. It takes a single argument, which is the name to use for the namespace. To omit the namespace wrapper, use the no_namespace attribute.

2.4.3 raw_interfaces_only

The raw_interfaces_only attribute will suppress generation of error-handling wrappers and __declspec(property) declarations using those wrappers. By default, the typelib headers are geared toward easy access to the contents of type libraries, hiding the HRESULT error codes and simplifying the need to worry about leaking interface pointers, VARIANTs, and BSTRs. Since some users are just interested in using #import as a replacement for the MIDL-generated headers now in use, or as a way to get access in C++ to objects which didn’t originate as IDL/ODL script, this attribute allows exposing just the raw low-level contents of a typelib.

The raw_interfaces_only attribute changes the default prefix used on the raw non-property methods. Normally, the prefix is raw_. This attribute causes no prefix to be used, so raw methods get the name actually used in the typelib.

2.4.4 raw_dispinterfaces

The raw_dispinterfaces attribute triggers generation of low-level wrappers for dispinterface methods and properties that call IDispatch::Invoke and return the HRESULT error code. Usually, only high-level wrappers are generated, which normally throw a C++ exception in case of failure.

2.4.5 rename(“OldName”,”NewName”)

The rename attribute is used to work around name collision problems by replacing all uses of a specific name from a typelib with another. It takes two arguments, the first being the old name from the typelib, the second the name to be used in its place.

This attribute is primarily useful in cases where a name from the typelib is also that of a macro definition in the system headers. This situation is apparent when errors or warnings like “not enough actual parameters for macro ‘OldName’” or “syntax error : ‘constant’” occur in the typelib headers.

Note that the replacement is for a name from the typelib, not for a name used in the typelib header. For instance, suppose a property named MyParent exists within a typelib, and that a macro GetMyParent is defined in some header used before the #import. Since GetMyParent is also the default name of the error-handling property get wrapper, a name collision will occur. To work around the problem, use rename(“MyParent”,”MyParentX”), and not rename(“GetMyParent”,”GetMyParentX”).

2.4.6 exclude(“Name1”,“Name2”, Ö)

The exclude attribute takes one or more arguments, each of which names a top-level typelib item (e.g., interfaces, coclasses, and typedefs). Each named item is excluded from the typelib headers.

This attribute is useful when importing typelibs generated by MIDL. In some cases, names from the system headers are also defined by these typelibs. If this leads to compile errors, exclude can be used to eliminate the troublesome items.

2.4.7 inject_statement(“source text”)

The inject_statement attribute inserts its single argument as raw source text into the typelib header, immediately following the start of the namespace that wraps the contents of the typelib headers.

2.4.8 named_guids

The named_guids attribute triggers generation of definitions and initializations of old-style GUID variables, giving them the form LIBID_MyLib, CLSID_MyCoClass, IID_MyInterface, and DIID_MyDispInterface.

2.4.9 no_implementation

The no_implementation attribute suppresses the generation of the .tli typelib header, which contains all the implementations of wrapper member functions. The .tlh typelib header, with all the declarations to expose typelib items, will be generated without a #include of the .tli header. See the description of the implementation_only attribute for a description of how to use this attribute.

2.4.10 implementation_only

The implementation_only attribute suppresses the generation of the .tlh typelib header, which contains all the declarations to expose typelib items. The .tli typelib header, with all the implementations of wrapper member functions, will still be generated. After generating the .tli, the compiler will directly include it, instead of including the .tlh header as normal.

The contents of the .tli header are wrapped by the same namespace that would normally wrap the .tlh header. In addition, the member functions are not declared as inline, as is normally done when the .tlh header includes the .tli header.

The implementation_only attribute is intended for use in conjunction with the no_implementation attribute, on paired #import directives, as a way of keeping the implementations out of the precompiled header file. A #import with the no_implementation attribute is put within the source region used to create the PCH, so it is used by a number of source files. A #import with the implementation_only attribute is then used, after the PCH region, in one source file only. This will generate all the wrapper member functions that might be required by other source files, but not require recompiling the implementations with each source.

Note that using a #import directive with implementation_only makes sense only when it follows, in the same source, a #import of the same typelib with the no_implementation attribute. Otherwise, the implementation_only #import will result in compile errors, since the wrapper class definitions will not be seen before the implementations of member functions of those classes.

2.4.11 raw_native_types

By default, the high-level error-handling methods use special classes in place of the raw BSTR and VARIANT data types and raw COM interface pointers. These classes, discussed later, handle the details of allocating and deallocating the raw data at the proper time, and greatly reduce the need to perform type casts and other conversions. The raw_native_types attribute is used to disable the use of these handler classes in the high-level method wrappers, and force the use of the low-level data types instead.

2.4.12 raw_method_prefix(“Prefix”)

By default, low-level properties and methods are exposed via member functions with a prefix of raw_, to avoid name collisions with the high-level error-handling member functions. The raw_method_prefix attribute is used to specify an alternate prefix. It takes a single argument, the prefix to be used. Note that the raw_interfaces_only attribute acts as if the attribute raw_method_prefix(“”) is also present.

2.4.13 high_method_prefix(“Prefix”)

By default, high-level error-handling properties and methods are exposed via member functions without a prefix, using the name from the typelib. The high_method_prefix attribute is used to specify a prefix to be used instead. It takes a single argument, the prefix to be used.

2.4.14 raw_property_prefixes(“GetPrefix”,”PutPrefix”,”PutRefPrefix”)

By default, low-level propget, propput, and propputref methods are exposed via member functions with prefixes of, respectively, get_, put_, and putref_, for compatibility with the names used in MIDL-generated headers. The raw_property_prefixes attribute is used to specify alternate prefixes for all three kinds of property methods. It takes three arguments, the prefixes to be used for propget, propput, and propputref methods.

2.4.15 high_property_prefixes(“GetPrefix”,”PutPrefix”,”PutRefPrefix”)

By default, high-level error-handling propget, propput, and propputref methods are exposed via member functions with prefixes of, respectively, Get, Put, and PutRef. The high_property_prefixes attribute is used to specify alternate prefixes for all three kinds of property methods. It takes three arguments, the prefixes to be used for propget, propput, and propputref methods.

3. Language Extensions

Since #import works by creating a header file which is then included, everything injected into the compilation stream by #import must be expressible as C++ source. This requires a few language extensions which are also available to a user outside of the context of #import. These extensions are all exposed via new __declspec attributes.

3.1 __declspec(selectany)

Normally, a global data item may be initialized once and only once in all the objects contributing to an EXE or DLL. This complicates initializing global data defined by the typelib header, since the same #import will likely appear in more than one source file. Without a language extension, a programmer would need to somehow flag one particular #import to define and initialize the global data, and all others to just declare and not define that data.

Instead, there’s a new __declspec attribute, __declspec(selectany). This can only be applied to the actual initialization of global data items that are externally visible. It causes the item to be specially flagged when emitted to the object file. The item is also forcibly emitted to the object even if no references are seen in that object. At link time, if multiple definitions of the item are seen, the linker picks one and discards the rest. Further, if linking -opt:ref, then transitive COMDAT elimination will cause any unreferenced selectany data items to be removed from the link output.

Some examples of __declspec(selectany):
// OK - x1 is initialized and visible externally
int __declspec(selectany) x1 = 1;
// error in C++ - const is by default static, so x2 isn’t visible externally
const int __declspec(selectany) x2 = 2;
// OK - x3 is extern const, so visible externally
extern const int __declspec(selectany) x3 = 3;
// OK - x4 is extern const, so visible externally
extern const int x4;
const int __declspec(selectany) x4 = 4;
// error - __declspec(selectany) must be on the initialization
int x5 = 5;
extern int __declspec(selectany) x5;

__declspec(selectany) is available in both the C and C++ compilers. While useful for COM support in the typelib header, where it is used for declaring old-style GUID constants if the named_guids attribute is used, it is actually useful far more generally.

3.2 __declspec(uuid(Ö)) and __uuidof(Ö)

Previously, GUIDs were defined for interfaces and coclasses via a naming convention, e.g., IID_IFoo and CLSID_Foo. Now, the compiler can natively support attaching a GUID to a struct or class that represents a COM item via a new __declspec attribute, __declspec(uuid(...)). The uuid attribute takes a string which names a GUID in normal registry format, with or without {} delimiters:

struct __declspec(uuid(“00000000-0000-0000-c000-000000000046”)) IUnknown;
struct __declspec(uuid(“{00020400-0000-0000-c000-000000000046}”)) IDispatch;

__declspec(uuid()) can be used on a declaration, as shown here, or a full definition, as shown in the COM interface example of section 0.

To retrieve the attached GUID, a new intrinsic, __uuidof(), is available. This takes as an argument a reference to an item which was declared with __declspec(uuid()). The argument may be the type name, or a pointer, reference, or array of that type, a template specialized on these types, or a variable of these types, as long as the compiler can resolve back to find the attached GUID.

__declspec(uuid()) is permitted on a redeclaration. This allows the system headers to supply the definitions of interfaces such as IUnknown, with a redeclaration in comdef.h or some related header later supplying IUnknown’s GUID.

3.3 __declspec(property(Ö)) and Data-Oriented Property Access

Interface and dispinterface properties can be accessed functionally through the HRESULT-handling wrappers, e.g.,

IBeeperPtr pBeep;
pBeep->PutSound(pBeep->GetSound() + 5);
It’s useful to make properties look like regular data members:
IBeeperPtr pBeep;
pBeep->Sound = pBeep->Sound + 5;
// or even
pBeep->Sound += 5;

This code fragment generates exactly the same code as the previous fragment, using the non-virtual wrapper functions.

The key to this is a way of declaring, in effect, “virtual data members” inside of interfaces. Yet another attribute, __declspec(property()), provides the linkage. It can be applied to non-static data members within a class or struct definition. It takes one or two arguments, one of which is get=get_func_name, the other put=put_func_name.

When the compiler sees a data member with the property attribute on the right-hand-side of a dot or points-to operator, it will convert the reference into the applicable get or put function, depending on whether the name is used in an lvalue or rvalue context. In more complicated contexts, such as +=, an appropriate rewrite is done to do both get and put. Note that expressions attempting to use the value of a property used in an lvalue context, as in a cascaded assign like pFoo->Prop1 = pFoo->Prop2 = 0; will not work, since this translates into pFoo->PutProp1(pFoo->PutProp2(0)), and the PutProp2 method generally returns void.

Parameterized properties are also handled through this mechanism. In this case, the data property is declared as an array with unspecified dimension. Multiple dimensions may be specified, but are unnecessary. When a parameterized data property is seen in an expression, the expressions used as array indices are collected and treated as function call arguments for the appropriate get or put function. For a put, the new value to be sent to the put function becomes the last argument, after the array index expressions. Function overloading resolution is then applied to check for legality, which is why the number of array dimensions doesn’t need to be declared up front.

__declspec(property(…)), while invented to support COM properties, is actually independent of COM. It could conceivably be used to write code that appears to be directly accessing a public data member, but is instead calling method functions which perform whatever extra processing is desired, e.g., data validation.

4. Runtime and Library Support

4.1 HRESULT Error Handling

The main point of the high-level COM interface wrappers is to hide the checking of HRESULT error codes. But what should occur when an error is detected? The HRESULT, IDispatch EXCEPINFO, and any IErrorInfo data should all be made available.

_com_raise_error is the support routine that dispatches an error detected by the typelib header error-handling methods or one of the COM support routines or native type wrapper classes. It is passed an HRESULT and a pointer to an IErrorInfo object (or NULL if no object is required/available). All it does is throw a _com_error object constructed from its arguments. A program can override this default behavior by supplying its own version of _com_raise_error.

_com_issue_error and _com_issue_errorex are the main support routine used to communicate errors from the typelib methods for vtable interfaces and native type wrapper classes to _com_raise_error. _com_issue_error takes an HRESULT, and passes it along without an IErrorInfo object to _com_raise_error. _com_issue_errorex takes an HRESULT, an interface pointer, and the interface’s IID. It checks if the interface supports error info on the given IID, and if yes retrieves the IErrorInfo object (if any). It then calls _com_raise_error with the HRESULT and IErrorInfo object.

For dispinterfaces, the runtime helpers that call IDispatch::Invoke handle a returned HRESULT of DISP_E_EXCEPTION by calling CreateErrorInfo to save the EXCEPINFO status in an IErrorInfo object. The low-level wrappers then call SetErrorInfo and return the HRESULT failure, so user code can call GetErrorInfo to retrieve the IErrorInfo object. The high-level wrappers just call _com_raise_error with the HRESULT and IErrorInfo object.

4.2 Smart Pointers

So far, what I've described is a pretty basic exposure of the contents of a typelib via struct definitions, with fairly minimal compiler extensions. Its main utility is in gathering together a number of disparate data sources, e.g., midl or mktyplib-generated headers, ClassWizard dispinterface access methods, and GUID definitions, into a single context, the typelib and #import-generated headers. That's a useful framework, but we'd like some higher-level support.

Primarily, we'd like a smart-pointer implementation which encapsulates interface pointers and eliminates AddRef, Release, and QueryInterface calls, as well as hides the CoCreateInstance call to create a new COM object. Such a smart-pointer is provided as template class _com_ptr_t in header comip.h

The linkage between the typelib header and the smart pointer implementation is provided through a number of standard macros. The header will contain lines like:

__COM_SMARTPTR_TYPEDEF(IFoo, __uuidof(IFoo));

This will by default expand to:

typedef _com_ptr_t<_com_IIID<IFoo, __uuidof(IFoo)> > IFooPtr;

Type IFooPtr can then be used in place of the raw interface pointer IFoo*, removing the need for calling the various IUnknown member functions.

Users wishing to use their own smart pointer implementation can override most parts of this expansion. The following lines appear in comdef.h:

#if !defined(_COM_SMARTPTR)
 #if !defined(_INC_COMIP)
  #include <comip.h>
 #endif
 #define _COM_SMARTPTR        _com_ptr_t
 #define _COM_SMARTPTR_LEVEL2 _com_IIID
#endif
#if defined(_COM_SMARTPTR)
 #if !defined(_COM_SMARTPTR_TYPEDEF)
  #if defined(_COM_SMARTPTR_LEVEL2
   #define _COM_SMARTPTR_TYPEDEF(Interface, IID) \
    typedef _COM_SMARTPTR<_COM_SMARTPTR_LEVEL2<Interface, &IID> > \
            Interface ## Ptr
  #else
   #define _COM_SMARTPTR_TYPEDEF(Interface, IID) \
    typedef _COM_SMARTPTR<Interface, &IID> \
            Interface ## Ptr
  #endif
 #endif
#endif

A program can define custom versions of macros _COM_SMARTPTR, _COM_SMARTPTR_LEVEL2, or _COM_SMARTPTR_TYPEDEF before the first #import or #include of comdef.h to modify the declaration of the smart pointer typedefs in the typelib header. The standard suffix used to name a smart pointer, Ptr, is not configurable, since that suffix is relied upon by the code that creates the typelib header.

The default smart pointer implementation has some knowledge of the VC++ COM runtime support. In contexts where an HRESULT cannot be returned, such as failure of a CoCreateInstance while constructing a new smart pointer, the error will instead be passed off to _com_issue_error.

The typelib header assumes the existence of smart pointers, as described in section 0. Specifically, whenever a COM method returns an interface pointer, the error-handling wrapper will instead return the appropriate smart pointer. This helps to make sure Release is called on the interface in case of exceptions or instances where the COM methods is used to generate a temporary interface pointer.

Smart pointer definitions are provided for all interfaces whose GUIDs are found in uuid.lib. These are found in comdef.h, along with the struct declarations for the uuid.lib interfaces and coclasses to attach the GUIDs to those structs using __declspec(uuid(…)).

4.3 Native Support for COM Types

Header comutil.h provides a pair of C++ classes that help use the COM types BSTR and VARIANT. BSTRs are wrapped by class _bstr_t, which hides the need for calling SysAllocString and SysFreeString, as well as providing a number of useful operators and other methods. VARIANTs are wrapped by class _variant_t, which takes care of doing VariantInit and VariantClear as necessary, and provides a large number of constructors, assignment operators, conversion extractors, and other methods.

As in the smart pointer case, these standard classes have knowledge of the VC++ COM runtime support to handle failure HRESULTs. For instance, when trying to assign a _variant_t to some other type, if the type coercion fails in VariantChangeType, the error will be passed off to _com_issue_error.

These standard classes are used in the typelib header high-level error-handling wrappers. For instance, a low-level method

HRESULT Method([in] BSTR bstr, [in] VARIANT vt, [out,retval] IFoo** ppFoo)

is exposed via the high-level wrapper

IFooPtr Method( _bstr_t bstr, _variant_t vt)

This use of wrapper classes can be disabled using the raw_native_types import attribute. There is no way to use the wrapper classes for the raw low-level methods.

5. Workarounds and Manual Editing

Occasionally, the typelib contains items that cannot be directly translated into C++ source. For instance, the names within a typelib are Unicode strings that may or may not be useable as C++ identifiers. In some cases, simple workarounds are automatically performed while creating the header files. In other cases, it’s left up to the user to find a workaround.

5.1 Duplicate enum Members

Some typelibs built by MKTYPLIB have multiple definitions of the same enum member name, within separate enums. For instance, grid32.ocx defines grdNone = 0 once in enum BorderStyleConstants, and again in enum ScrollbarsConstants. Since this is not legal in C++, the typelib header will comment out all duplicate enum member definitions after the first.

Sometimes, duplicate enum member names are found with different definitions. For instance, threed32.ocx defines _Inset = 0 in enum enumShadowStyleConstants, and later defines _Inset = 1 in enum enumBevelConstants. Again, all but the first definition is commented out, but in addition a #pragma message is injected which describes the name conflict.

5.2 Method/Property Name Conflicts with Enclosing Interface

Typelibs are able to have method or property names that duplicate the name of the enclosing interface or dispinterface. This is not allowed in C++, since it looks like an invalid constructor declaration. To avoid this, the typelib header will prefix the name of the offending method or property with a single underscore. No attempt is made to determine if the resulting name is still illegal for whatever reason.

5.3 Method/Property Name Conflicts with Non-enclosing Interface

Typelibs sometimes use the same name for either a property or method and some interface other than that enclosing the property or method. For instance, comctl32.ocx has, within dispinterface _DToolbar, a property defined as ImageList* ImageList, where ImageList is a dispinterface also defined within the typelib. The wrapped propget is ImageList* GetImageList(). That is invalid C++, since the ImageList property hides the ImageList interface while within _DToolbar. To work around this, the compiler will inject the record type before a user-defined name, e.g., struct ImageList* GetImageList().

5.4 Name Collisions and Illegal Identifiers

Names from the typelib are injected as-is into the header, except for the limited workarounds already mentioned. No attempt is made to detect and repair names which are not legal C++ identifiers, or which conflict with existing names, primarily those of #define macros. Instead, it is up to the user to repair these problems using the rename(“OldName”,”NewName”) import attribute. For instance, the Microsoft® Excel 5.0 typelib xl5en32.olb defines a method DialogBox. This conflicts with the macro of the same name, leading to compile errors. To fix this, use an attribute like rename(“DialogBox”,“DialogBoxXL”).

The compiler does not try to automatically handle these collisions with the preprocessor because it’s not clear what should be done, and because the same typelib header might work just fine in another compile with a different preprocessor context. Creating the typelib header is done independently of the current context, so the same typelib header is always created given the same input typelib and import attributes.

To fix a naming problem with the namespace used to wrap the typeinfo items, either the rename or rename_namespace import attributes may be used.

5.5 Unhandled typelib Problems

If the typelib header is not usable, for whatever reason, and #defines and import attribute are insufficient to work around the problem, the user will have to resort to manual editing. The idea is to create the .TLH and .TLI files via #import, then move the files out of the object directory, edit them appropriately, and change the #import to a #include. Obviously, this should be a last resort.

6. Support Classes

There are four classes defined in comdef.h and the associated COM support headers. Class _com_error defines the error object that is thrown by _com_raise_error in most failures. Template class _com_ptr_t wraps COM interface pointers, hiding the need for AddRef, Release, and QueryInterface. Classes _bstr_t and _variant_t wrap the BSTR and VARIANT types to provide resource management and other conveniences.

6.1 Class _com_error

A _com_error object represents an exception condition detected by the error-handling method wrappers from the typelib headers or by one the of COM support classes. The _com_error class encapsulates the HRESULT error code and any associated IErrorInfo object.

Constructors:

_com_error(HRESULT hr, IErrorInfo* perrinfo = NULL) throw()
_com_error(const _com_error& that) throw()

Constructs a _com_error object. The first constructor creates a new object given an HRESULT and optional IErrorInfo object. The second creates a copy of an existing object.

Destructor:

virtual ~_com_error() throw()

Destructs an existing _com_error object. The encapsulated IErrorInfo object, if present, is released.

Assignment operator:

_com_error& operator=(const _com_error& that) throw()

Assigns an existing _com_error object into another.

Extractors:

HRESULT Error() const throw()
IErrorInfo * ErrorInfo() const throw()
WORD WCode() const throw()

Retrieves the encapsulated items within a _com_error object. The Error and ErrorInfo methods return the raw HRESULT and IErrorInfo items as passed into the constructor.

The WCode method retrieves a 16-bit error code that has been mapped into the encapsulated HRESULT. If the HRESULT is within the range 0x80040200 to 0x8004FFFF, the WCode method returns the HRESULT minus 0x80040200, else it returns zero.

The WCode method exists to undo a mapping that happens in the COM support code. The wrapper for a dispinterface property or method calls a support routine which packages up the arguments and calls IDispatch::Invoke. On return, if a failure HRESULT of DISP_E_EXCEPTION is returned, the error information is retrieved from the EXCEPINFO structure passed to IDispatch::Invoke. The error code can either be a 16-bit value in EXCEPINFO.wCode or a full 32-bit value in EXCEPINFO.scode. If a 16-bit wCode is returned, it must first be mapped to a 32-bit failure HRESULT. This is done by adding 0x80040200 to the wCode, and capping the result at 0x8004FFFF.

IErrorInfo method intermediaries:

_bstr_t Description() const throw()
DWORD HelpContext() const throw()
_bstr_t HelpFile() const throw()
_bstr_t Source() const throw()
GUID GUID() const throw()

Calls the various IErrorInfo interface methods (e.g., Source calls IErrorInfo::GetSource). If no IErrorInfo is recorded within the _com_error object, return an empty _bstr_t, zero, or GUID_NULL as appropriate. Any failure while calling the IErrorInfo method is ignored.

FormatMessage accessors:

const TCHAR * ErrorMessage() const throw()

Returns the string message for the HRESULT recorded within the _com_error object by calling the Win32 API FormatMessage to retrieve the appropriate system message text. The string returned is allocated by the FormatMessage API, and will be released when the _com_error object is destroyed. If the HRESULT is a mapped 16-bit wCode (see the WCode method), then a generic message “IDispatch error #<wCode>” is returned. If no other message can be found, then a generic message “Unknown error #<hresult>” is returned. The returned string is either a Unicode or multi-byte string, depending on the state of the _UNICODE macro.

EXCEPINFO.wCode ? HRESULT mappers:

static HRESULT WCodeToHRESULT(WORD wCode) throw()
static WORD HRESULTToWCode(HRESULT hr) throw()

These two static member functions perform the 16-bit wCode to 32-bit HRESULT mapping, and vice-versa, as described above for the WCode method.

6.2 Class _bstr_t

A _bstr_t object encapsulates the BSTR data type. The class manages resource allocation and deallocation, calling SysAllocString, SysFreeString, and the other BSTR APIs as appropriate. The _bstr_t class uses reference counting to avoid excessive overhead.

Constructors:

_bstr_t() throw()

Constructs a default _bstr_t object, encapsulating a NULL BSTR.

_bstr_t(const _bstr_t& s) throw()

Constructs a _bstr_t object as a copy of another. This does a shallow copy, incrementing the reference count of the encapsulated BSTR instead of creating a new one.

_bstr_t(const char* s) throw(_com_error)
_bstr_t(const wchar_t* s) throw(_com_error)

Constructs a _bstr_t object, calling SysAllocString to create a new BSTR and encapsulate it in the new object. The char* constructor first does a multi-byte to Unicode conversion.

_bstr_t(const _variant_t& var) throw(_com_error)

Constructs a _bstr_t object from a _variant_t (see section 0), retrieving a BSTR from the encapsulated VARIANT.

_bstr_t(BSTR bstr, bool fCopy) throw(_com_error)

Constructs a _bstr_t object from an existing BSTR (as opposed to a simple wchar_t*). If fCopy is false, the incoming BSTR is attached to the new object, without making a new copy via SysAllocString. This is the method used by the typelib header wrappers to encapsulate and take ownership of a BSTR returned by an interface method as a _bstr_t.

Destructor:

~_bstr_t() throw()

Destructs an existing _bstr_t object. Decrements the reference count on the encapsulated BSTR, and if this is the last _bstr_t holding a reference, calls SysFreeString to release the BSTR.

Assignment operators:

_bstr_t& operator=(const _bstr_t& s) throw()
_bstr_t& operator=(const char* s) throw(_com_error)
_bstr_t& operator=(const wchar_t* s) throw(_com_error)
_bstr_t& operator=(const _variant_t& var) throw(_com_error)

Assigns a new value to an existing _bstr_t object, with the same semantics as the four corresponding constructors.

Operators:

_bstr_t& operator+=(const _bstr_t& s) throw(_com_error)
_bstr_t operator+(const _bstr_t& s) const throw(_com_error)
friend _bstr_t operator+(const char* s1, const _bstr_t& s2)
friend _bstr_t operator+(const wchar_t* s1, const _bstr_t& s2)

These two methods and two friend functions perform string concatenation. The first appends characters to the end of this object. The rest create a new _bstr_t.

Extractors:

operator const wchar_t*() const throw()
operator wchar_t*() const throw()
operator const char*() const throw(_com_error)
operator char*() const throw(_com_error)

These methods are used to extract raw pointers to the encapsulated Unicode BSTR or a multi-byte version of the same. They all do a shallow copy, returning a pointer to the actual internal buffer, so the resulting string should not be modified.

Comparison operators:

bool operator!() const throw()

Checks if the encapsulated BSTR is the NULL string, returning true if yes, false if not.

bool operator==(const _bstr_t& str) const throw()
bool operator!=(const _bstr_t& str) const throw()
bool operator<(const _bstr_t& str) const throw()
bool operator>(const _bstr_t& str) const throw()
bool operator<=(const _bstr_t& str) const throw()
bool operator>=(const _bstr_t& str) const throw()

These operators compare this object and another lexicographically, via wcscmp.

Low-level helper functions:

BSTR copy() const throw(_com_error)

Returns a newly allocated copy of the encapsulated BSTR. It is up to the caller to free the new copy.

unsigned int length() const throw()

Returns the length of the encapsulated BSTR, as returned by SysStringLen.

6.3 Class _variant_t

A _variant_t object encapsulates the VARIANT data type. The class manages resource allocation and deallocation, calling VariantInit and VariantClear as appropriate.

Constructors:

_variant_t() throw()

Constructs a default _variant_t object using VariantInit to set the VARIANT’s vt field to VT_EMPTY.

_variant_t(const VARIANT& varSrc) throw(_com_error)
_variant_t(const VARIANT* pSrc) throw(_com_error)
_variant_t(const _variant_t& varSrc) throw(_com_error)

Constructs a _variant_t object from a raw or encapsulated VARIANT using VariantCopy.

_variant_t(VARIANT& varSrc, bool fCopy) throw(_com_error)

Constructs a _variant_t object from an existing VARIANT. If fCopy is false, the incoming VARIANT is attached to the new object, without making a new copy via VariantCopy. This is the method used by the typelib header wrappers to encapsulate and take ownership of a VARIANT returned by an interface method as a _variant_t.

_variant_t(short sSrc, VARTYPE vtSrc = VT_I2) throw(_com_error)

Constructs a _variant_t object of type VT_I2 or VT_BOOL from a short. Any other VARTYPE results in an E_INVALIDARG error being raised.

_variant_t(long lSrc, VARTYPE vtSrc = VT_I4) throw(_com_error)

Constructs a _variant_t object of type VT_I4, VT_BOOL, or VT_ERROR from a long. Any other VARTYPE results in an E_INVALIDARG error being raised.

_variant_t(float fltSrc) throw()

Constructs a _variant_t object of type VT_R4 from a float.

_variant_t(double dblSrc, VARTYPE vtSrc = VT_R8) throw(_com_error)

Constructs a _variant_t object of type VT_R8 or VT_DATE from a double. Any other VARTYPE results in an E_INVALIDARG error being raised.

_variant_t(const CY& cySrc) throw()

Constructs a _variant_t object of type VT_CY from a CY item.

_variant_t(const _bstr_t& bstrSrc) throw(_com_error)
_variant_t(const wchar_t *pSrc) throw(_com_error)
_variant_t(const char* pSrc) throw(_com_error)

Constructs a _variant_t object of type VT_BSTR from a _bstr_t object or raw string. A new BSTR is allocated in all cases.

_variant_t(bool bSrc) throw()

Constructs a _variant_t object of type VT_BOOL from a bool.

_variant_t(IUnknown* pSrc, bool fAddRef = true) throw()
_variant_t(IDispatch* pSrc, bool fAddRef = true) throw()

Constructs a _variant_t object of type VT_UNKNOWN or VT_DISPATCH from a COM interface pointer. If fAddRef is true, then AddRef is called on the incoming pointer to match the call to Release that will occur when the _variant_t object is destructed, and it is up to the caller to call Release on the incoming argument. If fAddRef is false, this constructor in effect takes ownership of the incoming pointer, so the caller should not call Release on it.

_variant_t(const DECIMAL& decSrc) throw()

Constructs a _variant_t object of type VT_DECIMAL from a DECIMAL item.

_variant_t(BYTE bSrc) throw()

Constructs a _variant_t object of type VT_UI1 from a BYTE.

Destructor:

~_variant_t() throw(_com_error)

Destroys a _variant_t object by calling VariantClear to release any resources.

Extractors:

operator short() const throw(_com_error)
operator long() const throw(_com_error)
operator float() const throw(_com_error)
operator double() const throw(_com_error)
operator CY() const throw(_com_error)
operator bool() const throw(_com_error)
operator DECIMAL() const throw(_com_error)
operator BYTE() const throw(_com_error)

Extracts raw data from an encapsulated VARIANT. If the VARIANT is not already the proper type, VariantChangeType is used to attempt a conversion, and an error is raised on failure.

operator IUnknown*() const throw(_com_error)
operator IDispatch*() const throw(_com_error)

Extracts a COM interface or dispinterface pointer from an encapsulated VARIANT. AddRef is called on the pointer, so it is up to the caller to call Release to free the returned pointer. If the VARIANT is not already the proper type, VariantChangeType is used to attempt a conversion, and an error is raised on failure.

operator _bstr_t() const throw(_com_error)

Extracts a string from an encapsulated VARIANT. If the VARIANT is not already a VT_BSTR, VariantChangeType is used to attempt a conversion, and an error is raised on failure. The string is encapsulated in a _bstr_t object so it is properly destroyed on going out of scope.

Assignment operations:

_variant_t& operator=(const VARIANT& varSrc) throw(_com_error)
_variant_t& operator=(const VARIANT* pSrc) throw(_com_error)
_variant_t& operator=(const _variant_t& varSrc) throw(_com_error)
_variant_t& operator=(short sSrc) throw(_com_error)
_variant_t& operator=(long lSrc) throw(_com_error)
_variant_t& operator=(float fltSrc) throw(_com_error)
_variant_t& operator=(double dblSrc) throw(_com_error)
_variant_t& operator=(const CY& cySrc) throw(_com_error)
_variant_t& operator=(const _bstr_t& bstrSrc) throw(_com_error)
_variant_t& operator=(const wchar_t* pSrc) throw(_com_error)
_variant_t& operator=(const char* pSrc) throw(_com_error)
_variant_t& operator=(IDispatch* pSrc) throw(_com_error)
_variant_t& operator=(bool bSrc) throw(_com_error)
_variant_t& operator=(IUnknown* pSrc) throw(_com_error)
_variant_t& operator=(const DECIMAL& decSrc) throw(_com_error)
_variant_t& operator=(BYTE bSrc) throw(_com_error)

Assigns a new value to an existing _variant_t object.

Comparison operations:

bool operator==(const VARIANT& varSrc) const throw(_com_error)
bool operator==(const VARIANT* pSrc) const throw(_com_error)
bool operator!=(const VARIANT& varSrc) const throw(_com_error)
bool operator!=(const VARIANT* pSrc) const throw(_com_error)

Compares this _variant_t object with another, testing for equality or inequality.

Low-level operations:

void Clear() throw(_com_error)

Calls VariantClear on the encapsulated VARIANT object, releasing any resources held.

void Attach(VARIANT& varSrc) throw(_com_error)

Takes ownership of the VARIANT by encapsulating it. Releases any existing encapsulated VARIANT, then copies the argument VARIANT, and sets the argument VARIANT empty without releasing its resources.

VARIANT Detach() throw(_com_error)

Extracts and returns the encapsulated VARIANT, then clears this _variant_t object without releasing its resources. This removes the VARIANT from encapsulation, so it is up to the caller to release its resources via VariantInit.

void ChangeType(VARTYPE vartype, const _variant_t* pSrc = NULL)

throw(_com_error)

Calls VariantChangeType to force a _variant_t object to the indicated VARTYPE. If pSrc is NULL, the conversion is done in place, otherwise this _variant_t object is copied from the pSrc object and converted.

void SetString(const char* pSrc) throw(_com_error) 

Convert a multi-byte character string to a Unicode BSTR and assign to this _variant_t object.

6.4 Template class _com_ptr_t

A _com_ptr_t object encapsulates a COM interface pointer. The template class manages resource allocation and deallocation, calling the IUnknown members QueryInterface, AddRef, and Release as appropriate.

A smart pointer is usually referenced via the typedef definition provided by the _COM_SMARTPTR_TYPEDEF macro. This macro takes an interface name and the IID, and declares a _com_ptr_t specialization with the name of the interface and a suffix of Ptr. For example,

_COM_SMARTPTR_TYPEDEF(IMyInterface, __uuidof(IMyInterface));

will declare the _com_ptr_t specialization IMyInterfacePtr.

Constructors:

_com_ptr_t() throw()

Constructs a default, NULL, smart pointer.

template<> _com_ptr_t(const _com_ptr_t& cp) throw()

Constructs a smart pointer as a copy of another instance of the same smart pointer type. AddRef is called to increment the reference count for the encapsulated interface pointer.

_com_ptr_t(Interface* pInterface) throw()

Constructs a smart pointer from a raw interface pointer of this smart pointer’s type. AddRef is called to increment the reference count for the encapsulated interface pointer.

_com_ptr_t(Interface* pInterface, bool fAddRef) throw()

Constructs a smart pointer from a raw interface pointer of this smart pointer’s type. If fAddRef is true, AddRef is called to increment the reference count for the encapsulated interface pointer. If fAddRef is false, this constructor takes ownership of the raw interface pointer, without calling AddRef.

template<typename _InterfacePtr> _com_ptr_t(const _InterfacePtr& p)
throw(_com_error)

Constructs a smart pointer from a different smart pointer type or from a different raw interface pointer. QueryInterface is called to find an interface pointer of this smart pointer’s type. If the QueryInterface fails with E_NOINTERFACE, a NULL smart pointer results, otherwise a _com_error is raised.

_com_ptr_t(int null) throw(_com_error)

Constructs a NULL smart pointer. The null argument must be a zero.

template<> _com_ptr_t(const _variant_t& varSrc) throw(_com_error)

Constructs a smart pointer from a _variant_t object. The encapsulated VARIANT must have type VT_DISPATCH or VT_UNKNOWN, or be convertible to one of the two. If QueryInterface fails with E_NOINTERFACE, a NULL smart pointer results, otherwise a _com_error is raised.

explicit _com_ptr_t(const CLSID& clsid, DWORD dwClsContext = CLSCTX_ALL)
throw(_com_error)

Constructs a smart pointer given the CLSID of a coclass. This will call CoCreateInstance, via member CreateInstance, to create a new COM object and then query for this smart pointer’s interface type. If QueryInterface fails with E_NOINTERFACE, a NULL smart pointer results, otherwise a _com_error is raised.

explicit _com_ptr_t(LPOLESTR str, DWORD dwClsContext = CLSCTX_ALL)
throw(_com_error)

Constructs a smart pointer given a Unicode string which holds either a CLSID, if the string starts with ‘{‘, or a ProgID. This will call CoCreateInstance, via member CreateInstance, to create a new COM object and then query for this smart pointer’s interface type. If QueryInterface fails with E_NOINTERFACE, a NULL smart pointer results, otherwise a _com_error is raised.

explicit _com_ptr_t(LPCSTR str, DWORD dwClsContext = CLSCTX_ALL)
throw(_com_error)

Constructs a smart pointer given a multi-byte character string which holds either a CLSID, if the string starts with ‘{‘, or a ProgID. This will call CoCreateInstance, via member CreateInstance, to create a new COM object and then query for this smart pointer’s interface type. If QueryInterface fails with E_NOINTERFACE, a NULL smart pointer results, otherwise a _com_error is raised.

Destructor:

~_com_ptr_t() throw()

Destructs a smart pointer. If the encapsulated interface pointer isn’t NULL, calls Release to decrement the reference count.

Assignment operations:

template<> _com_ptr_t& operator=(const _com_ptr_t& cp) throw()

Sets a smart pointer to be a copy of another instance of the same smart pointer type. AddRef is called to increment the reference count for the encapsulated interface pointer, and Release is called to decrement the reference count for the previously encapsulated pointer.

_com_ptr_t& operator=(Interface* pInterface) throw()

Encapsulates a raw interface pointer of this smart pointer’s type. AddRef is called to increment the reference count for the encapsulated interface pointer, and Release is called to decrement the reference count for the previously encapsulated pointer.

template<typename _InterfacePtr> _com_ptr_t& operator=(const _InterfacePtr& p)
throw(_com_error)

Sets a smart pointer from a different smart pointer type or from a different raw interface pointer. QueryInterface is called to find an interface pointer of this smart pointer’s type, and Release is called to decrement the reference count for the previously encapsulated pointer. If QueryInterface fails with E_NOINTERFACE, a NULL smart pointer results, otherwise a _com_error is raised.

_com_ptr_t& operator=(int null) throw(_com_error)

Sets a smart pointer to NULL. The null argument must be a zero.

template<> _com_ptr_t& operator=(const _variant_t& varSrc) throw(_com_error)

Sets a smart pointer from a _variant_t object. The encapsulated VARIANT must have type VT_DISPATCH or VT_UNKNOWN, or be convertible to one of the two. If QueryInterface fails with E_NOINTERFACE, a NULL smart pointer results, otherwise a _com_error is raised.

Extractors:

operator Interface*() const throw()

Returns the encapsulated interface pointer, which may be NULL.

operator Interface&() const throw(_com_error)

Returns a reference to the encapsulated interface pointer, and issues an error if the pointer is NULL.

Interface& operator*() const throw(_com_error)

Allows this smart pointer object to act as though it were the actual encapsulated interface when dereferenced.

Interface* operator->() const throw(_com_error)

Allows this smart pointer object to act as though it were the actual encapsulated interface when dereferenced.

Interface** operator&() throw()

Releases any encapsulated interface pointer, replacing it with NULL, and returns the address of the encapsulated pointer. This allows the smart pointer to be passed by address to a function that has an out parameter through which it returns an interface pointer.

operator bool() const throw()

Allows this smart pointer object to be tested in a conditional expression. Returns true if the pointer is not NULL.

Comparison operations:

template<typename _InterfacePtr> bool operator==(_InterfacePtr p)
throw(_com_error)
template<> bool operator==(Interface* p) throw(_com_error)
template<> bool operator==(_com_ptr_t& p) throw()
template<> bool operator==(int null) throw(_com_error)
template<typename _InterfacePtr> bool operator!=(_InterfacePtr p)
throw(_com_error)
template<> bool operator!=(Interface* p) throw(_com_error)
template<> bool operator!=(_com_ptr_t& p) throw(_com_error)
template<> bool operator!=(int null) throw(_com_error)
template<typename _InterfacePtr> bool operator<(_InterfacePtr p)
throw(_com_error)
template<> bool operator<(Interface* p) throw(_com_error)
template<> bool operator<(_com_ptr_t& p) throw(_com_error)
template<> bool operator<(int null) throw(_com_error)
template<typename _InterfacePtr> bool operator>(_InterfacePtr p)
throw(_com_error)
template<> bool operator>(Interface* p) throw()
template<> bool operator>(_com_ptr_t& p) throw(_com_error)
template<> bool operator>(int null) throw(_com_error)
template<typename _InterfacePtr> bool operator<=(_InterfacePtr p)
throw(_com_error)
template<> bool operator<=(Interface* p) throw()
template<> bool operator<=(_com_ptr_t& p) throw(_com_error)
template<> bool operator<=(int null) throw(_com_error)
template<typename _InterfacePtr> bool operator>=(_InterfacePtr p)
throw(_com_error)
template<> bool operator>=(Interface* p) throw(_com_error)
template<> bool operator>=(_com_ptr_t& p) throw(_com_error)
template<> bool operator>=(int null) throw(_com_error)

Compare this smart pointer object to another smart pointer, raw interface pointer, or NULL. Except for the NULL pointer tests these all work by querying both pointers for IUnknown, and comparing the results.

template<typename _InterfaceType> bool operator==
(int null, _com_ptr_t<_InterfaceType>& p) throw(_com_error)
template<typename _Interface, typename _InterfacePtr> bool operator==
(_Interface* i, _com_ptr_t<_InterfacePtr>& p) throw(_com_error)
template<typename _Interface> bool operator!=
(int null, _com_ptr_t<_Interface>& p) throw(_com_error)
template<typename _Interface, typename _InterfacePtr> bool operator!=
(_Interface* i, _com_ptr_t<_InterfacePtr>& p) throw(_com_error)
template<typename _Interface> bool operator<
(int null, _com_ptr_t<_Interface>& p) throw(_com_error)
template<typename _Interface, typename _InterfacePtr> bool operator<
(_Interface* i, _com_ptr_t<_InterfacePtr>& p) throw(_com_error)
template<typename _Interface> bool operator>
(int null, _com_ptr_t<_Interface>& p) throw(_com_error)
template<typename _Interface, typename _InterfacePtr> bool operator>
(_Interface* i, _com_ptr_t<_InterfacePtr>& p) throw(_com_error)
template<typename _Interface> bool operator<=
(int null, _com_ptr_t<_Interface>& p) throw(_com_error)
template<typename _Interface, typename _InterfacePtr> bool operator<=
(_Interface* i, _com_ptr_t<_InterfacePtr>& p) throw(_com_error)
template<typename _Interface> bool operator>=
(int null, _com_ptr_t<_Interface>& p) throw(_com_error)
template<typename _Interface, typename _InterfacePtr> bool operator>=
(_Interface* i, _com_ptr_t<_InterfacePtr>& p) throw(_com_error)

These are not members of _com_ptr_t. Instead, they are function templates which allow comparison with a smart pointer on the right-hand side of the comparison operator.

Low-level operations:

HRESULT CreateInstance(const CLSID& rclsid, DWORD dwClsContext = CLSCTX_ALL)
throw()

Creates a new running instance of an object given a CLSID. This will call CoCreateInstance to create a new COM object and then query for this smart pointer’s interface type. The resulting pointer is then encapsulated within this smart pointer object. Release is called to decrement the reference count for the previously encapsulated pointer. This routine does not raise any error on failure, but instead returns the HRESULT.

HRESULT CreateInstance(LPOLESTR clsidString, DWORD dwClsContext = CLSCTX_ALL)
throw()

Creates a new running instance of an object given a Unicode string which holds either a CLSID, if the string starts with ‘{‘, or a ProgID. This will call CoCreateInstance to create a new COM object and then query for this smart pointer’s interface type. The resulting pointer is then encapsulated within this smart pointer object. Release is called to decrement the reference count for the previously encapsulated pointer. This routine does not raise any error on failure, but instead returns the HRESULT.

HRESULT CreateInstance(LPCSTR clsidStringA, DWORD dwClsContext = CLSCTX_ALL)
throw()

Creates a new running instance of an object given a multi-byte character string which holds either a CLSID, if the string starts with ‘{‘, or a ProgID. This will call CoCreateInstance to create a new COM object and then query for this smart pointer’s interface type. The resulting pointer is then encapsulated within this smart pointer object. Release is called to decrement the reference count for the previously encapsulated pointer. This routine does not raise any error on failure, but instead returns the HRESULT.

void Attach(Interface* pInterface) throw()

Encapsulates a raw interface pointer of this smart pointer’s type. AddRef is not called, so ownership of the interface is passed to the object. Release is called to decrement the reference count for the previously encapsulated pointer.

void Attach(Interface* pInterface, bool fAddRef) throw()

Encapsulates a raw interface pointer of this smart pointer’s type. If fAddRef is true, AddRef is called to increment the reference count for the encapsulated interface pointer. If fAddRef is false, this object takes ownership of the raw interface pointer, without calling AddRef. Release is called to decrement the reference count for the previously encapsulated pointer.

Interface* Detach() throw()

Extracts and returns the encapsulated interface pointer, then clears the encapsulated pointer storage to

NULL. This removes the interface pointer from encapsulation, so it is up to the caller to call Release.

void Release() throw(_com_error)

Calls IUnknown::Release on the encapsulated interface pointer, raising an E_POINTER error if the pointer is NULL.

void AddRef() throw(_com_error)

Calls IUnknown::AddRef on the encapsulated interface pointer, raising an E_POINTER error if the pointer is NULL.

Interface* GetInterfacePtr() const throw()

Returns the encapsulated interface pointer, which may be NULL.

template<typename _InterfaceType> HRESULT QueryInterface
(const IID& iid, _InterfaceType*& p) throw ()
template<typename _InterfaceType> HRESULT QueryInterface
(const IID& iid, _InterfaceType** p) throw()

Calls IUnknown::QueryInterface on the encapsulated interface pointer for the specified IID, returning the resulting raw interface pointer in p. This routine does not raise any error on failure, but instead returns the HRESULT.

7. Source Code Examples

7.1 Xltest.cpp: Microsoft Excel 97 Driver

This example demonstrates a real-world use of native COM support. It first spawns an instance of Microsoft Excel, makes it visible, and then populates a worksheet with some data. After pausing, xltest displays a chart built from the worksheet data, pauses a bit more, and then exits.

7.1.1 Xltest.bas

First, here’s the example written in Visual Basic:

Attribute VB_Name = "XLTest"
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Sub main()
Dim xl As Excel.Application
Dim Books As Workbooks
Dim Book As Workbook
Dim Sheet As Worksheet
Dim Range As Range
Dim Charts As Sheets
Dim Chart As Chart
Set xl = CreateObject("excel.application")
xl.Visible = True
Set Books = xl.Workbooks
Set Book = Books.Add(xlWorksheet)
Set Sheet = xl.ActiveSheet
Sheet.Range("A2").Value = "North"
Sheet.Range("B2").Value = "South"
Sheet.Range("C2").Value = "East"
Sheet.Range("D2").Value = "West"
Sheet.Range("A3").Value = 5.2
Sheet.Range("B3").Value = 10
Sheet.Range("C3").Value = 8
Sheet.Range("D3").Value = 20#
Sleep 2000
Set Range = Sheet.Range("A2:D3")
Set Charts = Book.Charts
Set Chart = Charts.Add
Chart.ChartWizard Range, xl3DPie, 7, xlRows, 1, 0, 2, "Sales Percentages"
 
Sleep 5000
 
Book.Saved = True
xl.Quit
End Sub

7.1.2 Xltest.cpp

Here’s the same example, this time written in C++ using native COM support.

// Compile: cl -GX -YX xltest.cpp
//
// Assumes Office 97 has been installed in "C:\Program Files"
#define IMPATTRS rename("DocumentProperties", "DocProps")
#import <C:\Program Files\Microsoft Office\Office\mso97.dll> IMPATTRS
#import <C:\Program Files\Common Files\Microsoft Shared\VBA\vbeext1.olb>
#import <C:\Program Files\Microsoft Office\Office\excel8.olb> IMPATTRS \
        rename("DialogBox", "DialogBoxXL") rename("RGB", "RBG_XL")
#include <stdio.h>
#pragma hdrstop()
class OleInit {
public:
    OleInit() { OleInitialize(NULL); }
    ~OleInit() { OleUninitialize(); }
} OleInitGlobal;
void dump_com_error(_com_error &);
void main()
{
    try {
        using namespace Excel;
        _ApplicationPtr pXL("Excel.Application");
        // The following line demonstrates calling a property-put method.
        // It translates to pXL->PutVisible(VARIANT_TRUE);
        pXL->Visible = true;
        // The following line demonstrates calling a property-get method.
        // It translates to pBooks = pXL->GetWorkbooks();
        WorkbooksPtr pBooks = pXL->Workbooks;
        _WorkbookPtr  pBook  = pBooks->Add((long)xlWorksheet);
        _WorksheetPtr pSheet = pXL->ActiveSheet;
        // The following series of lines demonstrate how parameterized
        // properties can be accessed using array indexing syntax.
        pSheet->Range["A2"]->Value = "North";
        pSheet->Range["B2"]->Value = "South";
        pSheet->Range["C2"]->Value = "East";
        pSheet->Range["D2"]->Value = "West";
        pSheet->Range["A3"]->Value = 5.2;
        pSheet->Range["B3"]->Value = (long)10;
        pSheet->Range["C3"]->Value = (short)8;
        pSheet->Range["D3"]->Value = (float)20.0;
        Sleep(2000);
        RangePtr  pRange  = pSheet->Range["A2"]["D3"];
        SheetsPtr pCharts = pBook->Charts;
        _ChartPtr pChart  = pCharts->Add();
        pChart->ChartWizard((IDispatch*)pRange, (long)xl3DPie, 7L,
                            (long)xlRows, 1L, 0L, 2L, "Sales Percentages");
        Sleep(5000);
        pBook->Saved = true;
        pXL->Quit();
    } catch(_com_error &e) {
        dump_com_error(e);
    }
}
void dump_com_error(_com_error &e)
{
    printf("Oops - hit an error!\n");
    printf("\tCode = %08lx\n", e.Error());
    printf("\tWCode = %04x\n", e.WCode());
    printf("\tCode meaning = %s\n", e.ErrorMessage());
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());
    printf("\tSource = %S\n", (wchar_t*)bstrSource);
    printf("\tDescription = %S\n", (wchar_t*)bstrDescription);
}

7.2 Test.tlh, test.tli: Example Typelib Headers

This example demonstrates how #import transforms an IDL script, by way of the binary type library, into the typelib headers.

7.2.1 Test.idl

First, here’s the IDL:

[
    uuid(e6457ff0-d8e9-11cf-82c6-00aa003d90f3),
    version(1.0),
    helpstring("COM Support Test")
]
library ComSupportTestLib
{
    importlib("stdole32.tlb");
    [ uuid(eec57af0-d8e9-11cf-82c6-00aa003d90f3), odl ]
    interface IMyInterface : IUnknown
    {
        [ propget ] HRESULT Sound( [out, retval] long *freq );
        [ propput ] HRESULT Sound( [in] long freq );
        HRESULT Method1( [in] long input );
        HRESULT Method2( [out, retval] long *output );
        HRESULT RetBSTR( [out, retval] BSTR *pbstr);
        HRESULT VarTest( [in] VARIANT var, [out, retval] VARIANT *pvar);
        HRESULT PtrTest( [out, retval] IMyInterface ** pinterface);
        long Query( [in] int index );
    };
    [ uuid(eec57af1-d8e9-11cf-82c6-00aa003d90f3) ]
    dispinterface IMyDispInterface
    {
    properties:
        [ id(1) ] long Sound;
    methods:
        [ id(2) ] void Method1( [in] long input );
        [ id(3) ] long Method2( );
        [ id(4) ] long Query( [in] int index );
        [ id(5) ] BSTR RetBSTR( );
        [ id(6) ] VARIANT VarTest( [in] VARIANT var );
        [ id(7) ] IMyDispInterface* PtrTest( );
        [ propget, id(8) ] long Channel( long index );
        [ propput, id(8) ] void Channel( long index, long value );
    };
    [ uuid(060247e0-d8ea-11cf-82c6-00aa003d90f3) ]
    coclass MyCoClass {
        [ default ] interface IMyInterface;
        dispinterface IMyDispInterface;
    };
}

7.2.2 Test.tlh

Next, here’s the TLH, the primary typelib header file. It has been created with the preprocessor directive #import “test.tlb”, without any import attributes.

// Created by Microsoft (R) C/C++ Compiler Version 11.00.0000 (3f78811f).
//
// test.tlh
//
// C++ source equivalent of Win32 type library test.tlb
// compiler-generated file created 01/23/97 at 17:19:26 - DO NOT EDIT!
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace ComSupportTestLib {
//
// Forward references and typedefs
//
struct __declspec(uuid("eec57af0-d8e9-11cf-82c6-00aa003d90f3"))
/* interface */ IMyInterface;
struct __declspec(uuid("eec57af1-d8e9-11cf-82c6-00aa003d90f3"))
/* dispinterface */ IMyDispInterface;
struct /* coclass */ MyCoClass;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(IMyInterface, __uuidof(IMyInterface));
_COM_SMARTPTR_TYPEDEF(IMyDispInterface, __uuidof(IDispatch));
//
// Type library items
//
struct __declspec(uuid("eec57af0-d8e9-11cf-82c6-00aa003d90f3"))
IMyInterface : IUnknown
{
    //
    // Property data
    //
    __declspec(property(get=GetSound,put=PutSound))
    long Sound;
    //
    // Wrapper methods for error-handling
    //
    long GetSound ( );
    void PutSound (
        long freq );
    HRESULT Method1 (
        long input );
    long Method2 ( );
    _bstr_t RetBSTR ( );
    _variant_t VarTest (
        const _variant_t & var );
    IMyInterfacePtr PtrTest ( );
    //
    // Raw methods provided by interface
    //
    virtual HRESULT __stdcall get_Sound (
        long * freq ) = 0;
    virtual HRESULT __stdcall put_Sound (
        long freq ) = 0;
    virtual HRESULT __stdcall raw_Method1 (
        long input ) = 0;
    virtual HRESULT __stdcall raw_Method2 (
        long * output ) = 0;
    virtual HRESULT __stdcall raw_RetBSTR (
        BSTR * pbstr ) = 0;
    virtual HRESULT __stdcall raw_VarTest (
        VARIANT var,
        VARIANT * pvar ) = 0;
    virtual HRESULT __stdcall raw_PtrTest (
        struct IMyInterface * * pinterface ) = 0;
    virtual long __stdcall Query (
        int index ) = 0;
};
struct __declspec(uuid("eec57af1-d8e9-11cf-82c6-00aa003d90f3"))
IMyDispInterface : IDispatch
{
    //
    // Property data
    //
    __declspec(property(get=GetChannel,put=PutChannel))
    long Channel[];
    __declspec(property(get=GetSound,put=PutSound))
    long Sound;
    //
    // Wrapper methods for error-handling
    //
    // Methods:
    HRESULT Method1 (
        long input );
    long Method2 ( );
    long Query (
        int index );
    _bstr_t RetBSTR ( );
    _variant_t VarTest (
        const _variant_t & var );
    IMyDispInterfacePtr PtrTest ( );
    long GetChannel (
        long index );
    void PutChannel (
        long index,
        long _arg2 );
    // Properties:
    long GetSound ( );
    void PutSound ( long _val );
};
struct __declspec(uuid("060247e0-d8ea-11cf-82c6-00aa003d90f3"))
MyCoClass;
    // [ default ] interface IMyInterface
    // dispinterface IMyDispInterface
//
// Wrapper method implementations
//
#include "test.tli"
} // namespace ComSupportTestLib
#pragma pack(pop)

7.2.3 Test.tli

Finally, here’s the TLI, the typelib header implementation file.

// Created by Microsoft (R) C/C++ Compiler Version 11.00.0000 (3f78811f).
//
// test.tli
//
// Wrapper implementations for Win32 type library test.tlb
// compiler-generated file created 01/23/97 at 17:19:26 - DO NOT EDIT!
#pragma once
//
// interface IMyInterface wrapper method implementations
//
inline long IMyInterface::GetSound ( ) {
    long _result;
    HRESULT _hr = get_Sound(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _result;
}
inline void IMyInterface::PutSound ( long freq ) {
    HRESULT _hr = put_Sound(freq);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
}
inline HRESULT IMyInterface::Method1 ( long input ) {
    HRESULT _hr = raw_Method1(input);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _hr;
}
inline long IMyInterface::Method2 ( ) {
    long _result;
    HRESULT _hr = raw_Method2(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _result;
}
inline _bstr_t IMyInterface::RetBSTR ( ) {
    BSTR _result;
    HRESULT _hr = raw_RetBSTR(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _bstr_t(_result, false);
}
inline _variant_t IMyInterface::VarTest ( const _variant_t & var ) {
    VARIANT _result;
    VariantInit(&_result);
    HRESULT _hr = raw_VarTest(var, &_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _variant_t(_result, false);
}
inline IMyInterfacePtr IMyInterface::PtrTest ( ) {
    struct IMyInterface * _result;
    HRESULT _hr = raw_PtrTest(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return IMyInterfacePtr(_result, false);
}
//
// dispinterface IMyDispInterface wrapper method implementations
//
inline HRESULT IMyDispInterface::Method1 ( long input ) {
    return _com_dispatch_method(this, 0x2, DISPATCH_METHOD, VT_EMPTY, NULL, 
        L"\x0003", input);
}
inline long IMyDispInterface::Method2 ( ) {
    long _result;
    _com_dispatch_method(this, 0x3, DISPATCH_METHOD, VT_I4, (void*)&_result,
        NULL);
    return _result;
}
inline long IMyDispInterface::Query ( int index ) {
    long _result;
    _com_dispatch_method(this, 0x4, DISPATCH_METHOD, VT_I4, (void*)&_result, 
        L"\x0003", index);
    return _result;
}
inline _bstr_t IMyDispInterface::RetBSTR ( ) {
    BSTR _result;
    _com_dispatch_method(this, 0x5, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
        NULL);
    return _bstr_t(_result, false);
}
inline _variant_t IMyDispInterface::VarTest ( const _variant_t & var ) {
    VARIANT _result;
    _com_dispatch_method(this, 0x6, DISPATCH_METHOD, VT_VARIANT,
        (void*)&_result, L"\x000c", &var);
    return _variant_t(_result, false);
}
inline IMyDispInterfacePtr IMyDispInterface::PtrTest ( ) {
    struct IMyDispInterface * _result;
    _com_dispatch_method(this, 0x7, DISPATCH_METHOD, VT_DISPATCH,
        (void*)&_result, NULL);
    return IMyDispInterfacePtr(_result, false);
}
inline long IMyDispInterface::GetChannel ( long index ) {
    long _result;
    _com_dispatch_method(this, 0x8, DISPATCH_PROPERTYGET, VT_I4,
        (void*)&_result, L"\x0003", index);
    return _result;
}
inline void IMyDispInterface::PutChannel ( long index, long _arg2 ) {
    _com_dispatch_method(this, 0x8, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, 
        L"\x0003\x0003", index, _arg2);
}
inline long IMyDispInterface::GetSound ( ) {
    long _result;
    _com_dispatch_propget(this, 0x1, VT_I4, (void*)&_result);
    return _result;
}
inline void IMyDispInterface::PutSound ( long _val ) {
    _com_dispatch_propput(this, 0x1, VT_I4, _val);
}

© 1997 Microsoft Corporation. All rights reserved.

The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.

This document is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.

Microsoft and Visual C++ are registered trademarks of Microsoft Corporation.

Other product or company names mentioned herein may be the trademarks of their respective owners.