Meilleures pratiques et exemples (SAL)Best Practices and Examples (SAL)

Voici quelques méthodes permettant de tirer le meilleur hors de la langue Annotation du Code Source (SAL) et éviter certains problèmes courants.Here are some ways to get the most out of the Source Code Annotation Language (SAL) and avoid some common problems.

_In__In_

Si la fonction est censée pour écrire à l’élément, utilisez _Inout_ au lieu de _In_.If the function is supposed to write to the element, use _Inout_ instead of _In_. Cela est particulièrement utile en cas de conversion automatique à partir des anciennes macros, à SAL.This is particularly relevant in cases of automated conversion from older macros to SAL. Avant de SAL, les programmeurs utilisaient des macros sous forme de commentaires, macros qui étaient nommés IN, OUT, IN_OUT, ou des variantes de ces noms.Prior to SAL, many programmers used macros as comments—macros that were named IN, OUT, IN_OUT, or variants of these names. Bien que nous vous conseillons de convertir ces macros à SAL, nous également vous recommande d’être prudent lorsque vous les convertissez, car le code peut ont été modifiés depuis le prototype d’origine a été écrite et l’ancienne macro peut ne plus refléter ce que fait le code.Although we recommend that you convert these macros to SAL, we also urge you to be careful when you convert them because the code might have changed since the original prototype was written and the old macro might no longer reflect what the code does. Soyez particulièrement vigilant le OPTIONAL macro de commentaire, car il est fréquemment placé de manière incorrecte, par exemple, sur l’autre côté d’une virgule.Be especially careful about the OPTIONAL comment macro because it is frequently placed incorrectly—for example, on the wrong side of a comma.


// Incorrect
void Func1(_In_ int *p1)
{
    if (p1 == NULL)
        return;

    *p1 = 1;
}

// Correct
void Func2(_Inout_ PCHAR p1)
{
    if (p1 == NULL)
        return;

    *p1 = 1;
}

_opt__opt_

Si l’appelant n’est pas autorisé à passer un pointeur null, utilisez _In_ ou _Out_ au lieu de _In_opt_ ou _Out_opt_.If the caller is not allowed to pass in a null pointer, use _In_ or _Out_ instead of _In_opt_ or _Out_opt_. Cela s’applique même à une fonction qui vérifie ses paramètres et retourne une erreur si elle a la valeur NULL lorsqu’il ne doit pas être.This applies even to a function that checks its parameters and returns an error if it is NULL when it should not be. Bien qu’une fonction recherchez son paramètre une valeur NULL inattendue et revenir normalement est une bonne pratique de codage défensive, cela ne signifie pas que l’annotation du paramètre peut être d’un type facultatif (_*Xxx*_opt_).Although having a function check its parameter for unexpected NULL and return gracefully is a good defensive coding practice, it does not mean that the parameter annotation can be of an optional type (_*Xxx*_opt_).


// Incorrect
void Func1(_Out_opt_ int *p1)
{
    *p = 1;
}

// Correct
void Func2(_Out_ int *p1)
{
    *p = 1;
}

_Pre_défensive_ et _Post_défensive__Pre_defensive_ and _Post_defensive_

Si une fonction s’affiche sur une limite d’approbation, nous vous recommandons d’utiliser le _Pre_defensive_ annotation.If a function appears at a trust boundary, we recommend that you use the _Pre_defensive_ annotation. Le modificateur « défensif » modifie certaines annotations pour indiquer que, au moment de l’appel, l’interface doit être strictement vérifiée, mais dans le corps de l’implémentation, il doit supposer que les paramètres incorrects peuvent être passés.The "defensive" modifier modifies certain annotations to indicate that, at the point of call, the interface should be checked strictly, but in the implementation body it should assume that incorrect parameters might be passed. Dans ce cas, _In_ _Pre_defensive_ est préférable à une limite d’approbation pour indiquer que, bien que l’appelant obtiendra une erreur si elle tente de passer la valeur NULL, le corps de fonction est analysé comme si le paramètre peut être NULL et toute tentative de suppression de la référence du pointeur sans d’abord vérifier l’absence de valeur NULL sera signalé.In that case, _In_ _Pre_defensive_ is preferred at a trust boundary to indicate that although a caller will get an error if it attempts to pass NULL, the function body will be analyzed as if the parameter might be NULL, and any attempts to de-reference the pointer without first checking it for NULL will be flagged. Un _Post_defensive_ annotation est également disponible pour une utilisation dans les rappels où la partie de confiance est supposée pour être l’appelant et le code non fiable est le code appelé.A _Post_defensive_ annotation is also available, for use in callbacks where the trusted party is assumed to be the caller and the untrusted code is the called code.

_Out_écrit__Out_writes_

L’exemple suivant montre une utilisation abusive courantes de _Out_writes_.The following example demonstrates a common misuse of _Out_writes_.


// Incorrect
void Func1(_Out_writes_(size) CHAR *pb,
    DWORD size
);

L’annotation _Out_writes_ signifie que vous disposez d’une mémoire tampon.The annotation _Out_writes_ signifies that you have a buffer. Il a cb octets alloués par le premier octet initialisé à la sortie.It has cb bytes allocated, with the first byte initialized on exit. Cette annotation n’est pas strictement incorrecte et il est utile exprimer la taille allouée.This annotation is not strictly wrong and it is helpful to express the allocated size. Toutefois, elle n’indique pas le nombre d’éléments est initialisé par la fonction.However, it does not tell how many elements are initialized by the function.

L’exemple suivant montre trois façons correctes pour spécifier complètement la taille exacte de la partie initialisée de la mémoire tampon.The next example shows three correct ways to fully specify the exact size of the initialized portion of the buffer.


// Correct
void Func1(_Out_writes_to_(size, *pCount) CHAR *pb,
    DWORD size,
    PDWORD pCount
);

void Func2(_Out_writes_all_(size) CHAR *pb,
    DWORD size
);

void Func3(_Out_writes_(size) PSTR pb,
    DWORD size
);

_Out_ PSTR_Out_ PSTR

L’utilisation de _Out_ PSTR est quasiment toujours incorrect.The use of _Out_ PSTR is almost always wrong. Ceci est interprété comme ayant un paramètre de sortie qui pointe vers une mémoire tampon de caractères et il est nul.This is interpreted as having an output parameter that points to a character buffer and it is NULL-terminated.


// Incorrect
void Func1(_Out_ PSTR pFileName, size_t n);

// Correct
void Func2(_Out_writes_(n) PSTR wszFileName, size_t n);

Comme une annotation _In_ PCSTR est commun et utile.An annotation like _In_ PCSTR is common and useful. Il pointe vers une chaîne d’entrée qui a de fin NULL, car la condition préalable de _In_ permet la reconnaissance d’une chaîne se terminant par NULL.It points to an input string that has NULL termination because the precondition of _In_ allows the recognition of a NULL-terminated string.

_Dans_ WCHAR * p_In_ WCHAR* p

_In_ WCHAR* p Indique qu’il existe un pointeur d’entrée p qui pointe vers un seul caractère._In_ WCHAR* p says that there is an input pointer p that points to one character. Toutefois, dans la plupart des cas, cela n’est probablement pas la spécification est destinée.However, in most cases, this is probably not the specification that is intended. Au lieu de cela, ce qui est probablement prévu est la spécification d’un tableau se terminant par NULL ; Pour cela, utilisez _In_ PWSTR.Instead, what is probably intended is the specification of a NULL-terminated array; to do that, use _In_ PWSTR.


// Incorrect
void Func1(_In_ WCHAR* wszFileName);

// Correct
void Func2(_In_ PWSTR wszFileName);

Il est courant de la spécification correcte de fin NULL manquante.Missing the proper specification of NULL termination is common. Utilisez la commande appropriée STR version pour remplacer le type, comme illustré dans l’exemple suivant.Use the appropriate STR version to replace the type, as shown in the following example.


// Incorrect
BOOL StrEquals1(_In_ PCHAR p1, _In_ PCHAR p2)
{
    return strcmp(p1, p2) == 0;
}

// Correct
BOOL StrEquals2(_In_ PSTR p1, _In_ PSTR p2)
{
    return strcmp(p1, p2) == 0;
}

_Out_plage__Out_range_

Si le paramètre est un pointeur et que vous souhaitez exprimer la plage de la valeur de l’élément qui est désignée par le pointeur, utilisez _Deref_out_range_ au lieu de _Out_range_.If the parameter is a pointer and you want to express the range of the value of the element that is pointed to by the pointer, use _Deref_out_range_ instead of _Out_range_. Dans l’exemple suivant, la plage de * pcbFilled est exprimée, pcbFilled pas.In the following example, the range of *pcbFilled is expressed, not pcbFilled.


// Incorrect
void Func1(
    _Out_writes_bytes_to_(cbSize, *pcbFilled) BYTE *pb,
    DWORD cbSize,
    _Out_range_(0, cbSize) DWORD *pcbFilled
);

// Correct
void Func2(
    _Out_writes_bytes_to_(cbSize, *pcbFilled) BYTE *pb,
    DWORD cbSize,
    _Deref_out_range_(0, cbSize) _Out_ DWORD *pcbFilled
);

_Deref_out_range_(0, cbSize) n’est pas strictement obligatoire pour certains outils, car il peut être déduit à partir de _Out_writes_to_(cbSize,*pcbFilled), mais il est affiché ici par souci d’exhaustivité._Deref_out_range_(0, cbSize) is not strictly required for some tools because it can be inferred from _Out_writes_to_(cbSize,*pcbFilled), but it is shown here for completeness.

Contexte incorrect dans _lorsque_Wrong context in _When_

Une autre erreur fréquente consiste à utiliser l’évaluation de l’état après les conditions préalables.Another common mistake is to use post-state evaluation for preconditions. Dans l’exemple suivant, _Requires_lock_held_ est une condition préalable.In the following example, _Requires_lock_held_ is a precondition.


// Incorrect
_When_(return == 0, _Requires_lock_held_(p->cs))
int Func1(_In_ MyData *p, int flag);

// Correct
_When_(flag == 0, _Requires_lock_held_(p->cs))
int Func2(_In_ MyData *p, int flag);

L’expression result fait référence à une valeur après l’état qui n’est pas disponible dans un état préalable.The expression result refers to a post-state value that is not available in pre-state.

TRUE dans _réussite_TRUE in _Success_

Si la fonction réussit lorsque la valeur de retour est différent de zéro, utilisez return != 0 en tant que la condition de réussite à la place de return == TRUE.If the function succeeds when the return value is nonzero, use return != 0 as the success condition instead of return == TRUE. Valeur différente de zéro ne signifie pas nécessairement équivalence à la valeur réelle que le compilateur fournit pour TRUE.Nonzero does not necessarily mean equivalence to the actual value that the compiler provides for TRUE. Le paramètre _Success_ est une expression, et les expressions suivantes sont évaluées comme équivalents : return != 0, return != false, return != FALSE, et return sans paramètres ou les comparaisons.The parameter to _Success_ is an expression, and the following expressions are evaluated as equivalent: return != 0, return != false, return != FALSE, and return with no parameters or comparisons.


// Incorrect
_Success_(return == TRUE, _Acquires_lock_(*lpCriticalSection))
BOOL WINAPI TryEnterCriticalSection(
  _Inout_ LPCRITICAL_SECTION lpCriticalSection
);

// Correct
_Success_(return != 0, _Acquires_lock_(*lpCriticalSection))
BOOL WINAPI TryEnterCriticalSection(
  _Inout_ LPCRITICAL_SECTION lpCriticalSection
);

Variable de référenceReference variable

Pour une variable de référence, la version précédente de SAL utilisé le pointeur implicite comme la cible de l’annotation et requis de l’ajout d’un __deref aux annotations attaché à une variable de référence.For a reference variable, the previous version of SAL used the implied pointer as the annotation target and required the addition of a __deref to annotations that attached to a reference variable. Cette version utilise l’objet lui-même et ne nécessite pas supplémentaires _Deref_.This version uses the object itself and does not require the additional _Deref_.


// Incorrect
void Func1(
    _Out_writes_bytes_all_(cbSize) BYTE *pb,
    _Deref_ _Out_range_(0, 2) _Out_ DWORD &cbSize
);

// Correct
void Func2(
    _Out_writes_bytes_all_(cbSize) BYTE *pb,
    _Out_range_(0, 2) _Out_ DWORD &cbSize
);

Annotations sur les valeurs de retourAnnotations on return values

L’exemple suivant montre un problème courant dans les annotations de valeur de retour.The following example shows a common problem in return value annotations.


// Incorrect
_Out_opt_ void *MightReturnNullPtr1();

// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();

Dans cet exemple, _Out_opt_ indique que le pointeur peut être NULL dans le cadre de la condition préalable.In this example, _Out_opt_ says that the pointer might be NULL as part of the precondition. Toutefois, les conditions préalables ne peut pas être appliqués à la valeur de retour.However, preconditions cannot be applied to the return value. Dans ce cas, l’annotation correcte est _Ret_maybenull_.In this case, the correct annotation is _Ret_maybenull_.

Voir aussiSee also

Utilisation d’Annotations SAL pour réduire les défauts du Code C/C++ comprendre SAL annotation des paramètres de fonction et valeurs de retour Annotation du comportement de la fonction annoter les Classes et Structs annotation du comportement de verrouillage Spécifiant le moment où une Annotation est applicable et fonctions intrinsèquesUsing SAL Annotations to Reduce C/C++ Code Defects Understanding SAL Annotating Function Parameters and Return Values Annotating Function Behavior Annotating Structs and Classes Annotating Locking Behavior Specifying When and Where an Annotation Applies Intrinsic Functions