Search code examples
c++comidl

defaultvalue and retval parameter ordering in IDL file and C++ header file


I'm trying to update an existing COM API to include a new optional output parameter, and have run into an issue with the enforced ordering of parameter types in the IDL file and the associated C++ header file.

Previously I had an IDL file like this (names changed to protect the innocent):

HRESULT CreateSomething(
    [in] BSTR base_uri,
    [in] ISomethingDescription* something_description,
    [out, retval] BSTR* something_uri
);

and the associated C++ header looked like:

HRESULT __stdcall CreateSomething(
    /* [in] */ BSTR study_uri,
    /* [in] */ ISomethingDescription* something_description,
    /* [out, retval] */ BSTR* something_uri
);

This needed to be updated to add an optional output parameter that could provide some clients with extra error reporting information, following an existing pattern against the rest of the in-house SDK.

To do this I was planning to update the IDL file to look like this:

HRESULT CreateSomething(
    [in] BSTR base_uri,
    [in] ISomethingDescription* something_description,
    [out, defaultvalue(0)] ErrorCode* error_code,
    [out, retval] BSTR* something_uri
);

Where ErrorCode is an enum defined in a separate IDL file. This follows the guidance I have seen online about how parameters with defaultvalue and retval attributes should be ordered. However, when I then try to upate the C++ header file, I run into the issue that the default parameter is not at the end of the parameter list, i.e.

HRESULT __stdcall CreateSomething(
    /* [in] */ BSTR study_uri,
    /* [in] */ ISomethingDescription* something_description,
    /* [out, defaultvalue(0)] */ ErrorCode* error_code = 0,   // this is clearly wrong
    /* [out, retval] */ BSTR* something_uri
);

The documentation I have seen on MSDN seems to indicate that you can use parameters with defaultvalue and retval attributes within the same function definition, and I have seen some examples of IDL files that contain such definitions, but I cannot work out how it is possible to write the equivalent C++ definition.

Some clients (and our own test code) use the MIDL generated header files directly, so if I omit the default values from the original C++ header file, the generated function in the MIDL generated file does not contain a default value entry, i.e. it looks like this:

virtual HRESULT STDMETHODCALLTYPE CreateSomething( 
            /* [in] */ BSTR base_uri,
            /* [in] */ ISomethingDescription *something_description,
            /* [defaultvalue][out] */ ErrorCode *error_code,
            /* [retval][out] */ BSTR *something_uri) = 0;

Similar functions in our SDK include the default values in the IDL file and the C++ header - not to say that that whole approach isn't questionable.

Any help/advice on this would be greatly appreciated.


Solution

  • MSDN is pretty clear about the parameters:

    The MIDL compiler accepts the following parameter ordering (from left-to-right):

    1. Required parameters (parameters that do not have the [defaultvalue] or [optional] attributes),
    2. optional parameters with or without the [defaultvalue] attribute,
    3. parameters with the [optional] attribute and without the [defaultvalue] attribute,
    4. [lcid] parameter, if any,
    5. [retval] parameter

    Note that there is no mention for "not optional default value" parameter. This is because default values apply to optional only - makes sense because a parameter which is not optional always have the explicit value, with no defaults applicable.

    So your default value parameter has to be optional, and then new constraints are applicable: "The [optional] attribute is valid only if the parameter is of type VARIANT or VARIANT *.", which means that your optional enum parameter is basically invalid. It might so happen that MIDL compiler accepts this and put the respective flag on the type library, but eventually this is not how it is expected to work in first place: default values only apply to variants.

    Then when you have C++ header generated from IDL, you will find out that optional parameters is simply a markup. Development environments such as scripting languages might detect it in order to update syntax for this COM method respectively, but on C++ side the parameter is always present and is basically mandatory. The only thing you basically have there is special convention to see that caller did not have the value for optional parameter without default value, in which case you have VT_ERROR, DISP_E_PARAMNOTFOUND in the respective variant parameter.