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:
- Implementation: You should not use this.
- _Pre_/_Post_: Low-level annotations; for specific use cases.
- _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:
_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
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.
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.
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
.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
_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.
_Ret_notnull_ int* Foo()
{
return new int[3];
}
_Ret_valid_
A single value is returned, which is allocated and initialized.
_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.
_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.
_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.
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
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.
_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.
_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.
__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.
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.
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_
.
_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.
typedef _Return_type_success_(return >= 0) LONG HRESULT;
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_
).
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_
).
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.
void Foo(_Out_ int* param1);
#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