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(NULL)
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_FROM_WIN32(RPC_X_NULL_REF_POINTER)
.
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_ERROR
/DISP_E_PARAMNOTFOUND
.
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_X_NULL_REF_POINTER
error
raised only sometimes?
0 comments