Consider the following interface declaration in an IDL file:
// Code in italics is wrong interface IFoo : IUnknown { HRESULT Cancel([in, optional, string] LPCWSTR pszReason); };
The idea here is that you want to be able to call the Cancel
method as pFoo->Cancel(
if you don’t want to provide a reason.
If you try this, you’ll find that the call sometimes fails with error 0x800706F4, which decodes to HRESULT_
. What’s going on here?
The optional
attribute does not mean what you think it means. To a C or C++ programmer, an “optional” pointer parameter typically means that it is valid to pass NULL
/nullptr
as the parameter value. But that’s not what it means to the IDL compiler.
To the IDL compiler, optional parameters are hints to the scripting engine that the parameter should be passed as VT_
/DISP_
. The attribute is meaningful only when applied to parameters of type VARIANT
or VARIANT*
.
What you actually want is the unique
attribute. This somewhat confusingly-named attribute means “The parameter is allowed to be a null pointer.” Therefore, the interface should have been written as
interface IFoo : IUnknown
{
HRESULT Cancel([in, unique, string] LPCWSTR pszReason);
};
At the lowest level in the marshaler, pointer parameters are marked as ref
, unique
, or ptr
. ref
parameters may not be null, whereas unique
and ptr
parameters are allowed to be null. Larry Osterman explained to me that the default for interface pointers (anything derived from IUnknown
) is unique
and the default for all other pointer types is ref
. Therefore, if you want to say that NULL
is a valid value for a non-interface pointer parameter, you must say so explicitly by annotating the parameter as [unique]
.
It’s probably too late to change the behavior of MIDL to reject the [optional]
tag on non-VARIANT
parameters because in the decades since the attribute was introduced, it’s probably being used incorrectly approximately twenty-five bazillion times, and making it an error would break a lot of code. (Even if you just made it a warning, that wouldn’t help because a lot of people treat warnings as errors.)
Exercise: Why is the RPC_
error raised only sometimes?
0 comments