Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VisualC++

SAL 2 Function Parameters Annotations

5.00/5 (8 votes)
16 Apr 2015CPOL15 min read 43.8K  
Explanation of the different SAL annotations for function parameters for Visual Studio Code Analysis

Introduction

The Microsoft source-code annotation language (SAL) is a way for developers to annotate their functions, structs and types. This serves as documentation about the expected values and pre/post-conditions. It is also used by Visual Studio Code Analysis to check the usage for any errors.

This article is about the annotations for function parameters. There is relatively little information about how to correctly annotate functions, and especially some of the more complex annotations are difficult to understand. In this article I will describe most of the annotations, often with sample code to show their correct usage.

The annotation language was greatly improved with SAL 2.0, which can be used starting with Visual Studio 2012. This article only covers that version. A porting guide from SAL 1 can be found here.

There are 3 layers of annotations:

  1. Implementation: You should not use this.
  2. _Pre_/_Post_: Low-level annotations; for specific use cases.
  3. _In_/_Out_/_Ret_: Use these, unless an annotation from the middle layer fits better.

Additionally, there are structural annotations, which modify the other annotations for specific use cases.

Remarks on Usage

The annotations should be used to describe how the function is supposed to be used. Even if your code checks for wrong usage, the annotations should describe the correct usage. For example, a parameter might be checked for nullptr (even though a value should always be passed), and perhaps returns an error code. However, the annotation should still be _In_, not _In_opt_.

The annotations can sometimes describe more than the (currently released) analyzers can use. Nonetheless, you should use the most specific annotation, if possible. For example, the analyzer in Visual Studio 2013 treats _Out_writes_, _Out_writes_to_ and _Out_writes_all_ the same. But - if possible - you should the latter, more specific annotations, both for better documentation and for future updates to the analyzer which might use the additional information.

Conditions can be checked before the function is called (preconditions) and after the function has returned (postconditions). In conditions are preconditions. Inout conditions are pre- and postconditions. Out/return conditions are postconditions.

_In_ / _Out_ / _Ret_ Layer

Use these annotations in most cases.

The _opt_ variants of the annotations describe an optional parameter, i.e. the value is allowed to be nullptr. However, only use these if it is a valid function call. If the parameter is checked for null and an error code is returned, do not use the optional annotations.

Normally, any parameter counts are given in number of elements. The _bytes_ variants of the annotations are used to give the parameter counts in bytes.

Some annotations take parameters for a count/size. These can be constants, the names of other parameters, or even simple expressions which result in an integer value. For example:

C++
_In_reads_(3) POINT* param
_In_reads_(*param_count) POINT* param, _In_ int *param_count
_In_reads_(rows * columns) POINT* param, size_t rows, size_t columns
_In_reads_(param - end) POINT* param, POINT* end   // _In_reads_to_ptr_(end) would be the better annotation in this case

Some annotations are for null-terminated values. Any counts should be including the null-terminator.

_In_ Annotations

The _In_ annotations are used for function parameters where the data will be read, but not modified.

_In_

A parameter of which a single value will be read. The parameter is not allowed to be null.

C++
void Foo(_In_ int param1, _In_ POINT* param2)
{
    int a = param1;
    LONG b = param2->x;
}

Note that the annotation on the first parameter is not needed, since it is not a pointer or reference.

_In_opt_

The same as _In_, except that the parameter is optional, i.e. nullptr is allowed.

C++
void Foo(_In_opt_ POINT* param1)
{
    if (param1 != nullptr)
        LONG a = param1->x;
}

_In_z_ / _In_opt_z_

A null-terminated input parameter.

Note that for character arrays, the PSTR types (LPSTR, LPWSTR, etc.) already have the correct annotation on their typedef. So you should use _In_ LPSTR param instead of _In_z_ char* param.

C++
void Foo(_In_z_ char* param1)
{
    char* a = _strdup(param1);
}

_In_reads_ / _In_reads_opt_

An input parameter where (potentially) more than one element will be read. The annotation's parameter describes the number of elements.

C++
void Foo(_In_reads_(3) POINT* param1)
{
    LONG sum = 0;
    for (int i = 0; i < 3; i++)
        sum += param1[i].x;
}

_In_reads_bytes_ / _In_reads_bytes_opt_

A variant of _In_reads_ / _In_reads_opt_ where the annotation's parameter describes the number of bytes instead of elements.

C++
void Foo(_In_reads_bytes_(3 * sizeof(POINT)) POINT* param1)
{
    LONG sum = 0;
    for (int i = 0; i < 3; i++)
        sum += param1[i].x;
}

_In_reads_z_ / _In_reads_opt_z_

A null-terminated input parameter where (potentially) more than one element will be read. The annotation's parameter describes the number of elements.

C++
void Foo(_In_reads_z_(3) char* param1)
{
    char a[4] = {};
    for (int i = 0; i < 3; i++)
        a[i] = param1[i];
}

_In_reads_or_z_ / _In_reads_or_z_opt_

A null-terminated input parameter where (potentially) more than one element will be read. The parameter is read either up to the given length, or until the null-terminator is reached - whichever is first. The annotation's parameter describes the (maximum) number of elements.

C++
void Foo(_In_reads_or_z_(3) char* param1)
{
    char a[4] = {};
    for (int i = 0; i < 3 && param1[i] != '\0'; i++)
        a[i] = param1[i];
}

_In_reads_to_ptr_ / _In_reads_to_ptr_opt_

An input parameter which will be read until the given end pointer, which is passed as the annotation's parameter.

C++
void Foo(_In_reads_to_ptr_(param1_end) POINT* param1, POINT* param1_end)
{
    LONG sum = 0;
    for (auto current = param1; current != param1_end; current++)
        sum += current->x;
}

_In_reads_to_ptr_z_/_In_reads_to_ptr_opt_z_

A variant of _In_reads_to_ptr_/_In_reads_to_ptr_opt_ where the parameter is null-terminated.

_Out_ Annotations

The _Out_ annotations are used for parameters where data is written to, in order to pass data back to the caller. The caller has to supply the buffer where the data will be written to.

_Out_ / _Out_opt_

A single value is written at the address given by the parameter.

C++
void Foo(_Out_ int* param1, _Out_opt_ int* param2)
{
    *param1 = 42;
    if (param2 != nullptr)
        *param2 = 42;
}

_Out_writes_ / _Out_writes_opt_

An output parameter where more than one element will be written. The annotation's parameter describes the number of elements. The actual number of elements written is not specified.

C++
// (_Out_writes_all_(3) int* param1) would be better
void Foo(_Out_writes_(3) int* param1)
{
    for (int i = 0; i < 3; i++)
        param1[i] = 42;
}

_Out_writes_z_ / _Out_writes_z_opt_

A variant of _Out_writes_ / _Out_writes_opt_ where the parameter will be null-terminated.

_Out_writes_to_ / _Out_writes_to_opt_

An output parameter where only some elements will be written. The annotation's first parameter is the (input) size of the given array, while the second parameter is the number of elements (from the start) which have been written.

C++
int Foo(_Out_writes_to_(param1_count, return) int* param1, int param1_count)
{
    int retval = 0;
    for (int i = 0; i < param1_count; i++, retval++)
    {
        if (rand() % 2)
            break;
        param1[i] = 42;
    }

    return retval;
}

_Out_writes_all_ / _Out_writes_all_opt_

A variant of _Out_writes_ / _Out_writes_opt_ where the annotation parameter describes the number of elements known to be written after the function returns.

_Out_writes_to_ptr_ / _Out_writes_to_ptr_opt_

A variant of _Out_writes_to_ / _Out_writes_to_opt_ where the annotation parameter describes the end pointer, i.e. the decorated parameter will be written to until the end pointer is reached.

C++
void Foo(_Out_writes_to_ptr_(param1_end) int* param1, int* param1_end)
{
    for (auto current = param1; current != param1_end; current++)
        current[i] = 42;
}

_Out_writes_bytes_ / _Out_writes_bytes_opt_ / _Out_writes_bytes_to_ / _Out_writes_bytes_to_opt_ / _Out_writes_bytes_all_ / _Out_writes_bytes_all_opt_

A variant of _Out_writes_ / _Out_writes_opt_ / _Out_writes_to_ / _Out_writes_to_opt_ / _Out_writes_all_ / _Out_writes_all_opt_ where the annotation's parameter describes the number of bytes instead of elements.

_Inout_ Annotations

The _Inout_ annotations are used for parameters where data is both read from and written to.

_Inout_ / _Inout_opt_

A single value is read from and written to.

C++
void Foo(_Inout_ int* param1, _Inout_opt_ int* param2)
{
    *param1 += 42;
    if (param2 != nullptr)
        *param2 += 42;
}

_Inout_z_ / _Inout_opt_z_

A variant of _Inout_ / _Inout_opt_ where the parameter is (and stays) null-terminated. This should be used for in-place updates, ex. toupper(_Inout_z_ char* sz).

_Inout_updates_ / _Inout_updates_opt_ / _Inout_updates_z_ / _Inout_updates_opt_z_

A variant of _Out_writes_ / _Out_writes_opt_ / _Out_writes_z_ / _Out_writes_z_opt_ for input/output parameters.

_Inout_updates_to_ / _Inout_updates_to_opt_ / _Inout_updates_all_ / _Inout_updates_all_opt_

A variant of _Out_writes_to_ / _Out_writes_to_opt_ / _Out_writes_all_ / _Out_writes_all_opt_ for input/output parameters.

_Inout_updates_bytes_ / _Inout_updates_bytes_opt_ / _Inout_updates_bytes_to_ / _Inout_updates_bytes_to_opt_ / _Inout_updates_bytes_all_ / _Inout_updates_bytes__all_opt_

A variant of _Out_writes_ / _Out_writes_opt_ / _Out_writes_to_ / _Out_writes_to_opt_ / _Out_writes_all_ / _Out_writes_all_opt_ for input/output parameters, where the annotation's parameter describes the number of bytes instead of elements.

_Outptr_ Annotations

The _Outptr_ annotations are used for parameters where the called function creates the buffer and returns the pointer to that buffer to the caller.

By default, it is implied that a buffer is always allocated. If a function might not allocate a buffer in all cases, but can also store a nullptr (for example if the function fails), there are _result_maybenull_ / _maybenull_ variants of the annotations. In that case, the caller needs to check for nullptr before using the buffer.

If a parameter is set to nullptr when the function fails, the parameter should be further decorated with _Outptr_result_nullonfailure_ or _Outptr_opt_result_nullonfailure_ for required or optional parameters, respectively.

_Outptr_ / _Outptr_result_maybenull_ / _Outptr_opt_ / _Outptr_opt_result_maybenull_

A single element is allocated and written by the function. The pointer to that element is stored in the adress given by the function parameter.

C++
void Foo(_Outptr_ POINT** param1, _Outptr_result_maybenull_ int** param2)
{
    *param1 = new POINT;
    (*param1)->x = 42;
    (*param1)->y = 42;

    if (rand() % 2)
        *param2 = new int{ 42 };
    else
        *param2 = nullptr;
}

_Outptr_result_z_ / _Outptr_result_maybenull_z_ / _Outptr_opt_result_z_ / _Outptr_opt_result_maybenull_z_

A variant of _Outptr_ / _Outptr_result_maybenull_ / _Outptr_opt_ / _Outptr_opt_result_maybenull_ where a null-terminated value is allocated.

_COM_Outptr_ / _COM_Outptr_result_maybenull_ / _COM_Outptr_opt_ / _COM_Outptr_opt_result_maybenull_

A variant of _Outptr_ / _Outptr_result_maybenull_ / _Outptr_opt_ / _Outptr_opt_result_maybenull_ where a ref-counted COM object is allocated. It is implied that the parameter is set to nullptr on failure.

_Outptr_result_buffer_ / _Outptr_result_buffer_maybenull_ / _Outptr_opt_result_buffer_ / _Outptr_opt_result_buffer_maybenull_

More than one element can be allocated by the function. The annotation's parameter describes the number of elements which will be allocated. The actual number of elements written is not specified.

C++
// (_Outptr_result_buffer_all_(3) int** param1) would be better
void Foo(_Outptr_result_buffer_(3) int** param1)
{
    *param1 = new int[3];
    (*param1)[0] = 42;
    (*param1)[1] = 42;
    (*param1)[2] = 42;
}

_Outptr_result_buffer_to_ / _Outptr_result_buffer_to_maybenull_ / _Outptr_opt_result_buffer_to_ / _Outptr_opt_result_buffer_to_maybenull_

A variant where not all of the allocated buffer's elements might get written. The annotation's first parameter is the size of the allocated array, while the second parameter is the number of elements (from the start) which have been written.

C++
int Foo(_Outptr_result_buffer_to_(param1_count, return) int** param1, int param1_count)
{
    *param1 = new int[param1_count];
    int retval = 0;
    for (int i = 0; i < param1_count; i++, retval++)
    {
        if (rand() % 2)
            break;
        (*param1)[i] = 42;
    }

    return retval;
}

_Outptr_result_buffer_all_ / _Outptr_result_buffer_all_maybenull_ / _Outptr_opt_result_buffer_all_ / _Outptr_opt_result_buffer_all_maybenull_

A variant of _Outptr_result_buffer_ / _Outptr_result_buffer_maybenull_ / _Outptr_opt_result_buffer_ / _Outptr_opt_result_buffer_maybenull_ where the number of elements allocated and written is known.

_Outptr_result_bytebuffer_ / _Outptr_result_bytebuffer_maybenull_ / _Outptr_opt_result_bytebuffer_ / _Outptr_opt_result_bytebuffer_maybenull_ / _Outptr_result_bytebuffer_to_ / _Outptr_result_bytebuffer_to_maybenull_ / _Outptr_opt_result_bytebuffer_to_ / _Outptr_opt_result_bytebuffer_to_maybenull_ / _Outptr_result_bytebuffer_all_ / _Outptr_result_bytebuffer_all_maybenull_ / _Outptr_opt_result_bytebuffer_all_ / _Outptr_opt_result_bytebuffer_all_maybenull_

A variant of _Outptr_result_buffer_ / _Outptr_result_buffer_maybenull_ / _Outptr_opt_result_buffer_ / _Outptr_opt_result_buffer_maybenull_ / _Outptr_result_buffer_to_ / _Outptr_result_buffer_to_maybenull_ / _Outptr_opt_result_buffer_to_ / _Outptr_opt_result_buffer_to_maybenull_ / _Outptr_result_buffer_all_ / _Outptr_result_buffer_all_maybenull_ / _Outptr_opt_result_buffer_all_ / _Outptr_opt_result_buffer_all_maybenull_ where the annotation's parameter describes the number of bytes instead of elements.

_Outref_ Annotations

The _Outref_ annotations are used for parameters where the called function creates the buffer and stores the pointer to that buffer in the reference given by the caller. The annotations work the same way as the similarly named _Outptr_ variants, except that a reference is used (which cannot be nullptr).

By default, it is implied that a buffer is always allocated. If a function might not allocate a buffer in all cases, but can also store a nullptr (for example if the function fails), there are _result_maybenull_ / _maybenull_ variants of the annotations. In that case, the caller needs to check for nullptr before using the buffer.

If a parameter is set to nullptr when the function fails, the parameter should be further decorated with _Outref_result_nullonfailure_.

_Outref_ / _Outref_result_maybenull_

A single element is allocated and written by the function. The pointer to that element is stored in the reference given by the function parameter.

C++
void Foo(_Outref_ POINT*& param1, _Outref_result_maybenull_ int*& param2)
{
    param1 = new POINT;
    param1->x = 42;
    param1->y = 42;

    if (rand() % 2)
        param2 = new int{ 42 };
    else
        param2 = nullptr;
}

_Outref_result_buffer_ / _Outref_result_buffer_maybenull_

More than one element can be allocated by the function. The annotation's parameter describes the number of elements which will be allocated. The actual number of elements written is not specified.

C++
// (_Outref_result_buffer_all_(3) int*& param1) would be better
void Foo(_Outref_result_buffer_(3) int*& param1)
{
    param1 = new int[3];
    param1[0] = 42;
    param1[1] = 42;
    param1[2] = 42;
}

_Outref_result_buffer_to_ / _Outref_result_buffer_to_maybenull_

A variant where not all of the allocated buffer's elements might get written. The annotation's first parameter is the size of the allocated array, while the second parameter is the number of elements (from the start) which have been written.

C++
int Foo(_Outref_result_buffer_to_(param1_count, return) int*& param1, int param1_count)
{
    param1 = new int[param1_count];
    int retval = 0;
    for (int i = 0; i < param1_count; i++, retval++)
    {
        if (rand() % 2)
            break;
        param1[i] = 42;
    }

    return retval;
}

_Outref_result_buffer_all_ / _Outref_result_buffer_all_maybenull_

A variant of _Outref_result_buffer_ / _Outref_result_buffer_maybenull_ where the number of elements allocated and written is known.

_Outref_result_bytebuffer_ / _Outref_result_bytebuffer_maybenull_ / _Outref_result_bytebuffer_to_ / _Outref_result_bytebuffer_to_maybenull_ / _Outref_result_bytebuffer_all_ / _Outref_result_bytebuffer_all_maybenull_

A variant of _Outref_result_buffer_ / _Outref_result_buffer_maybenull_ / _Outref_result_buffer_to_ / _Outref_result_buffer_to_maybenull_ / _Outref_result_buffer_all_ / _Outref_result_buffer_all_maybenull_ where the annotation's parameter describes the number of bytes instead of elements.

_Ret_ Annotations

The _Ret_ annotations are used for annotating the return value of the function, when the function returns a buffer. The annotations are mostly similar to the _Outptr_ / _Outref_ annotations.

By default, it is implied that a buffer is always allocated/returned. If a function might not allocate a buffer in all cases, but can also return a nullptr (for example if the function fails), there are _maybenull_ variants of the annotations. In that case, the caller needs to check for nullptr before using the buffer.

_Ret_z_ / _Ret_maybenull_z_

A null-terminated value is returned.

C++
_Ret_z_ char* Foo()
{
    return _strdup("42");
}

_Ret_maybenull_z_ char* Bar()
{
    if (rand() % 2)
        return _strdup("42");
    else
        return nullptr;
}

_Ret_notnull_ / _Ret_maybenull_ / _Ret_null_

A value is returned which is not nullptr / might be a nullptr / is a nullptr, respectively. However, that value is not initialized.

These are used for allocation functions, for example.

C++
_Ret_notnull_ int* Foo()
{
    return new int[3];
}

_Ret_valid_

A single value is returned, which is allocated and initialized.

C++
_Ret_valid_ int* Foo()
{
    return new int{ 42 };
}

_Ret_writes_ / _Ret_writes_maybenull_

More than one element is allocated and completely initialized. The annotation's parameter describes the number of elements.

This annotation is thus similar to _Outptr_result_buffer_all_ / _Outptr_result_buffer_all_maybenull_. If the number of initialized elements is not known, you need to use _Ret_notnull_ / _Ret_maybenull_ together with a _Post_writable_size_ annotation.

C++
_Ret_writes_(3) int* Foo()
{
    auto retval = new int[3];
    retval[0] = 42;
    retval[1] = 42;
    retval[2] = 42;
}

_Ret_writes_z_ / _Ret_writes_maybenull_z_

A variant of _Ret_writes_ / _Ret_writes_maybenull_ where the return value is null-terminated.

_Ret_writes_to_ / _Ret_writes_to_maybenull_

A variant where not all of the allocated buffer's elements might get written. The annotation's first parameter is the size of the allocated array, while the second parameter is the number of elements (from the start) which have been written.

C++
_Ret_writes_to_(42, *param1) int* Foo(_Out_ int* param1)
{
    auto retval = new int[42];
    auto valid_count = 0;
    for (int i = 0; i < 42; i++, valid_count++)
    {
        if (rand() % 2)
            break;
        retval[i] = 42;
    }

    *param1 = valid_count;
    return retval;
}

_Ret_writes_bytes_ / _Ret_writes_bytes_maybenull_ / _Ret_writes_bytes_to_ / _Ret_writes_bytes_to_maybenull_

A variant of _Ret_writes_bytes_ / _Ret_writes_bytes_maybenull_ / _Ret_writes_bytes_to_ / _Ret_writes_bytes_to_maybenull_ where the annotation's parameter describes the number of bytes instead of elements.

Other Annotations

_Check_return_

The caller needs to check the return value. This is used when a function returns an error/success code, for example.

_Must_inspect_result_

A variant of _Check_return_ for the function's parameters.

_Printf_format_string_ / _Scanf_format_string_ / _Scanf_s_format_string_

The parameter is a string which should be interpreted as a format string used by the printf / scanf / scanf_s family of functions, respectively. The additional arguments, as requested by the format specifiers, are assumed to be directly after the annotated parameter. They can be either as va_list or as a variadic argument list.

C++
void Foo(_In_ _Printf_format_string_ LPCSTR param1, va_list args)
{
    vprintf(param1, args);
}

_Printf_format_string_params_ / _Scanf_format_string_params_ / _Scanf_s_format_string_params_

A variant of _Printf_format_string_ / _Scanf_format_string_ / _Scanf_s_format_string_ when the additional arguments are not directly after the annotated parameter.

The annotation's parameter is used for specifying the location of the additional arguments:

  • Variadic arguments: 0
  • va_list: The index of the va_list parameter, counting from the annotated parameter
C++
void Foo(int param1, _In_ _Printf_format_string_params_(0) LPCSTR param2, int param3, ...);
void Bar(int param1, _In_ _Printf_format_string_params_(2) LPCSTR param2, int param3, va_list param4);

_In_range_ / _Out_range_ / _Ret_range_

Specifies the valid range of input values for an input value or the valid range of returned values for an output/return value, respectively. The annotation's parameters are either:

  • Minimum and maximum value (inclusive).
  • Comparison operator (==, >, etc.) and value.
C++
_Ret_range_(>= , 0) int Foo(_In_range_(0, 255) int param1, _Out_range_(< , 0) int& param2)
{
    param2 = -param1;
    return 0;
}

_Deref_in_range_ / _Deref_out_range_ / _Deref_ret_range_

A variant of _In_range_ / _Out_range_ / _Ret_range_ where the range applies to the value pointed to by the parameter.

_Pre_equal_to_ / _Post_equal_to_

A shortcut for _In_range_ / _Out_range_ when the range is equal to a value.

_Pre_satisfies_ / _Post_satisfies_

Allows writing your own pre-/postcondition.

_Pre_ / _Post_ Layer

These annotations are more lower-level than the ones in the _In_ / _Out_ layer. In some cases they can express more. Therefore, I will not mention or explain all of the annotations.

Normally, any parameter counts are given in number of elements. The _bytes_ / _byte_ variants of the annotations are used to give the parameter counts in bytes.

The _Pre_ annotations describe the state of the annotated parameter before the function is called, while the _Post_ annotations describe the state after the function returns.

The annotations can be placed both on the function's parameters as well as on the function's return value.

_Pre_readable_size_

Specifies that the buffer can be read by the function, with the annotation's parameter describing the size of the buffer. Use the _In_reads_ variants instead.

_Pre_writable_size_

Specifies that the buffer can be written to by the function, with the annotation's parameter describing the size of the buffer. However, the state of the buffer after the function returns is not specified (in contrast to _Out_writes_ or _Ret_writes_).

void Foo(_Pre_writable_size_(3) int* param1
{
    if (rand() % 2)
    {
        param1[0] = 42;
        param1[1] = 42;
        param1[2] = 42;
    }
}

_Post_readable_size

Specifies that the buffer can be read when the function returns, with the annotation's parameter describing the valid size of the buffer. However, the state of the buffer before the function called (including its size) is not specified (in contrast to _Out_writes_, etc.).

Normally there is no reason to use this variant instead of _Out_writes_, etc.

_Post_writable_size_

Specifies that the returned buffer can be written to, with the annotation's parameter describing the size of the buffer. However, the state of the buffer before the function called (including its size) is not specified (in contrast to _Out_writes_, etc.).

These are used for allocation functions, for example.

C++
_Post_writable_size_(3) int* Foo()
{
    return new int[3];
}

_Pre_readable_byte_size_ / _Pre_writable_byte_size_ / _Post_readable_byte_size_ / _Post_writable_byte_size_

A variant of _Pre_readable_size_ / _Pre_writable_size_ / _Post_readable_size_ / _Post_writable_size_ where the annotation's parameter describes the number of bytes instead of elements.

_Pre_z_

Specifies that the buffer is null-terminated when the function is called.

This can be used in combination with _In_reads_bytes_, for example, since there is no _In_reads_bytes_z_ annotation.

_Pre_notnull_ / _Pre_maybenull_ / _Pre_null_

Specifies that the buffer is not nullptr, maybe nullptr, is nullptr, respectively, when the function is called.

These are used for deallocation functions, for example.

_Post_z_

Specifies that the buffer will be null-terminated when the function returns.

This can be used in combination with _Out_writes_bytes_, for example, since there is no _Out_writes_bytes_z_ annotation.

This can also be used when using the __DEFINE_CPP_OVERLOAD_SECURE_FUNC_ macros, since the size is only internally visible to the template.

C++
__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, strcpy_s, _Post_z_ char, _Dest, _In_z_ const char *, _Source)

_Post_invalid_ / _Post_ptr_invalid_

Specifies that the buffer will be invalid when the function returns.

These are used for deallocation functions, for example.

_Prepost_z_

The combination of _Pre_z_ and _Post_z_, i.e. it specifies that the buffer is both null-terminated when the function is called and when the function returns.

Structural annotations

These annotations are used to modify other annotations. Therefore, these annotations usually take another annotation as parameter.

_At_

Specifies that the annotation given as second parameter applies to the target given in the first parameter.

This can be used when a function works with class members, for example.

C++
class Foo
{
public:
    Foo()
    {
        m_items = new int[1];
    }

    _At_(this->m_items, _Out_) void Bar()
    {
        this->m_items[0] = 42;
    }

    int *m_items;
};

_At_buffer_

Specifies that the annotation applies to each of the elements of a buffer. It is defined as _At_buffer_(target, iter, bound, annos): target is the name of the buffer; iter is a variable name usable inside the annotation; bound is the ending bound of the buffer; annos is the annotation to apply.

_At_buffer_(_S, _Iter_, _N, _Post_satisfies_(_S[_Iter_] == _C))
wchar_t * wmemset(wchar_t *_S, wchar_t _C, size_t _N);

_When_

Specifies that the annotation given as second parameter only applies when the expression given as first parameter evaluates to non-zero.

C++
void Foo(_When_(param2, _Out_) int* param1, bool param2)
{
    if (param2)
        param1[0] = 42;
}

_Success_

Specifies under what conditions a function was successful, which is given as the annotation's parameter. If the function was not successful, any post-conditions do not apply.

If a type is known to be a success status code, then the typedef can be annotated using _Return_type_success_. In that case, using _Success_ is not necessary. However, if _Success_ used, it overrides _Return_type_success_.

C++
_Success_(return)
bool Foo(_Out_ int* param1)
{
    if (rand() % 2 != 0)
        return false;

    param1[0] = 42;
    return true;
}

_Return_type_success_

This is applied to a typedef. It specifies the success state for any function returning that type.

C++
typedef _Return_type_success_(return >= 0) LONG HRESULT; // Already defined this way in the CRT.

// _Success_ not needed.
HRESULT Foo(_Out_ int* param1)
{
    if (rand() % 2 != 0)
        return E_FAIL;

    param1[0] = 42;
    return S_OK;
}

_On_failure_

Specifies that the annotation given as parameter only applies when the function fails (as described by _Success_ or _Return_type_success_).

C++
HRESULT Foo(_Out_writes_all_(3) _On_failure_(_Out_) int* param1)
{
    param1[0] = 42;
    if (rand() % 2 != 0)
        return E_FAIL;

    param1[1] = 42;
    param1[2] = 42;
    return S_OK;
}

_Always_

Specifies that the annotation given as parameter applies when the function fails and when the function succeeds (as described by _Success_ or _Return_type_success_).

C++
HRESULT Foo(_Always_(_Out_) int* param1, _Out_ int* param2)
{
    param1[0] = 42;
    if (rand() % 2 != 0)
        return E_FAIL;

    param2[0] = 42;
    return S_OK;
}

_Use_decl_annotations_

This can only be applied to a function definition. It specifies that the annotations of the function's declaration should be (re)used. That way, the annotations do not need to be duplicated.

For this to work, the declaration needs to be in scope, and no other annotations are allowed on the definition.

C++
// Foo.h
void Foo(_Out_ int* param1);

// Foo.cpp
#include "Foo.h"

_Use_decl_annotations_
void Foo(int* param1)
{
    param1[0] = 42;
}

Acknowledgements

Many thanks to the Code Analysis team at Microsoft, for answering my questions, specifically Eric Battalio, Dave Sielaff, James McNellis and Joe Morris.

History

2015-04-16 Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)