What will be placed in the output parameter if the function fails?

Raymond

A customer wanted to know what, if anything, can be said about the value in the output parameter of the Open­Process­Token function if the call fails.

According to the ground rules, the contents of output parameters are unspecified on failure. There is a special rule for COM which says that the contents of output parameters on failure must be valid values for their type. For example, if the output parameter is a COM interface pointer, then it must contain a valid value for a COM interface pointer on return, even if the call failed.¹

However, as a concession to the widespread use of RAII types, the de facto rule is that if the output parameter is a resource that requires management (disposal, release, etc.), then the value on failure is either unchanged or set to a “no resource” value.

If you use an RAII type, then you are going to pass an empty² RAII object to the function because the function may put a valid object into the RAII object, and you don’t want to leak the old object. Therefore, if the function that fails leaves the RAII object empty or explicitly sets it to empty, the RAII object can be destructed without harm. On the other hand, if the function put garbage in the output parameter, then the RAII type is going to try to clean up a garbage resource, which is unlikely to end well.

In other words, for types that require cleanup, “empty stays empty” on failure. (On the other hand, if the original object was not empty, it’s not specified whether the result is empty or not.)

Now, if the output doesn’t require resource management, then this rule doesn’t apply. For example, if the output is a string buffer, the buffer could very well be filled with garbage on failure. You need to free the buffer, and a buffer of garbage can be freed just as easily as a buffer with a valid string in it.

Examples:

BOOL GetSomething(HSOMETHING* result);

Does the HSOMETHING need to be destroyed in a special way? If so, then on failure, Get­Something will either leave the *result unchanged, or set it to nullptr. If you are using an RAII type to hold the HSOMETHING, then you will have arranged for *result to contain nullptr before calling Get­Something, which means that on failure, *result will still be nullptr.

HRESULT GetWidgetName(HWIDGET widget, wchar_t buffer[], size_t size);

Since wchar_t does not require any special resource management, the buffer could be filled with garbage if Get­Widget­Name fails.

HRESULT GetWidgetName(HWIDGET widget, PWSTR* name);

This case is different. Let’s say that the name needs to be freed with a function like Co­Task­Mem­Free. In that case, the function cannot set *name to a garbage pointer, because that would cause Co­Task­Mem­Free to crash. On failure, *name will be unchanged or set to nullptr.

Bonus chatter: WIL has a number of helper functions for manipulating tokens.

¹ For COM interface pointers, the value is typically nullptr on failure. Exceptions are called out in the function documentation if the result is anything else. For example, a function might return E_PENDING and put a provisional answer in the output pointer, with a complete answer provided when the operation completes.

² The word empty is a term of art which refers to the case where an object of an RAII type is not managing anything.

 

3 comments

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

  • pnoop star

    Are there any functions that you know of that set the contents of an output structure to safe values on failure?

    struct Blob { int len; void *buf; };
    Blob b;
    GetBlob(97, &b);
    

    where GetBlob() fills in the Blob with some useful values on success, and when it fails it initializes the Blob to { 0, NULL }.
    That way if I ignore the result code, I won’t get a catastrophic uninitialized pointer error when I use the Blob. Yes, yes don’t ignore errors, but I’m just wondering if you have seen this pattern in any Windows APIs.

    • Erik Fjeldstrom

      Off the top of my head I can’t think of any, but I make use of the fact that Windows functions (like C library ones) usually don’t scribble on passed-in memory regions on errors by pre-initializing them to a known value, usually with

      Blob b={0};

      or (if the function fills in a pointer for ppFoo parameters)

      Blob *b=NULL;

      If you encounter a function that uses caller memory for stuff, the only suggestion I have is to code defensively. Fortunately that is fairly rare in my experience.

    • Dave Gzorple

      We do something similar in our APIs, the first bit of code that gets executed sets all output parameters to -1, NULL, or { 0x00, 0x00, … } as appropriate. This means that even if the caller doesn’t check the return value – and for many trivial calls no-one ever does, do you ever check the return value from printf()? – they trigger an error if they try and act on the returned data.