I marked my parameter as [optional], so why do I get an RPC error when I pass NULL?

Raymond Chen


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_PARAM­NOT­FOUND. 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?


Comments are closed. Login to edit/delete your existing comments