Compartilhar via


Práticas recomendadas e exemplos (SAL)

Aqui estão algumas maneiras de obter ao máximo fora da linguagem de anotação de código-fonte (SAL) e para evitar alguns problemas comuns.

_In_

Se a função deve escrever no elemento, use _Inout_ em vez de _In_.Isso é especialmente relevante no caso de conversão automatizados de um macros mais antigos ao SAL.Antes de SAL, muitos desenvolvedores tenham usados como macros os comentário- macros que foram chamados IN, OUT, IN_OUT, ou variantes desses nomes.Embora tenhamos recomendemos que você converter esses macros ao SAL, nós também incitamo-lo ser cuidadoso quando você converte os porque o código pode ter sido alterado desde que o protótipo original foi escrito e a macro antigo pode ainda não refletir o que o código faz.É especialmente cuidadoso a macro de comentário de OPTIONAL porque ele é colocado geralmente incorreto- por exemplo, no lado errado de uma vírgula.

// 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_

Se não é permitido para o chamador passar em um ponteiro zero, em um uso _In_ ou em _Out_ em vez de _In_opt_ ou de _Out_opt_.Isso se aplica mesmo uma função que verifica seus parâmetros e retorna um erro se é NULO quando não deve ser.Embora ter uma verificação de função seu parâmetro para NULL e retorno inesperados é normalmente uma boa prática de codificação defensiva, não significa que a anotação de parâmetro pode ser de um tipo opcional (_Xxx_opt_).

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

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

_Pre_defensive_ e _Post_defensive_

Se uma função aparece em um limite de confiança, é recomendável usar a anotação de _Pre_defensive_ .O modificador “de defesa altera determinadas” anotações para indicar que, no ponto de chamada, a interface deve ser marcada restrita, mas o corpo de implementação deve presumir que os parâmetros incorretos podem ser passados.Nesse caso, _In_ _Pre_defensive_ seja preferencial em um limite de confiança indicar que embora um chamador obtém um erro se tentar passar NULL, o corpo da função será analisado como se o parâmetro pode ser NULO, e qualquer de- referência tenta o ponteiro sem primeiro verifique para NULL será embandeirada.Uma anotação de _Post_defensive_ , também está disponível para uso em callbacks onde a parte de confiança é assumida para o chamador e não confiável é o código chamado.

_Out_writes_

O exemplo a seguir demonstra um uso indevido comuns de _Out_writes_.

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

A anotação _Out_writes_ significa que você tem um buffer.Tem bytes de cb alocados, com o primeiro byte inicializado na saída.Esta anotação não é estritamente e errada é útil expressar o tamanho atribuído.No entanto, não com quantos elementos são inicializados pela função.

O exemplo a seguir mostra três maneiras de especificar corretas totalmente o tamanho exato da parte inicializada de 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

O uso de _Out_ PSTR é quase sempre incorreto.Isso é interpretado como o ter um parâmetro de saída que aponta para um caractere armazenar em buffer e NULL- é encerrado.

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

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

Uma anotação como _In_ PCSTR é comum e útil.Aponta para a entrada uma cadeia de caracteres que tem a finalização NULA porque a condição anterior de _In_ permite o reconhecimento de uma cadeia de caracteres NULL- finalizada.

_In_ WCHAR* p

_In_ WCHAR* p informa que há um ponteiro p de entrada que aponta para um caractere.No entanto, na maioria dos casos, isso não é provavelmente a especificação que destina-se.Em vez disso, o que é destinado provavelmente é a especificação de uma matriz NULL- finalizada; para fazer isso, use _In_ PWSTR.

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

// Correct
void Func2(_In_ PWSTR wszFileName);

Perder a especificação apropriada de fim NULA é comum.Use a versão apropriada de STR para substituir o tipo, conforme mostrado no exemplo o seguir.

// 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_range_

Se o parâmetro é um ponteiro e você deseja expressar o intervalo valor do elemento que está apontada pelo ponteiro, use _Deref_out_range_ em vez de _Out_range_.No exemplo, o intervalo de *pcbFilled é expresso, não 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ão é estritamente necessária para algumas ferramentas como pode ser inferido de _Out_writes_to_(cbSize,*pcbFilled), mas é mostrado aqui para a abrangência.

Errado no contexto _When_

Outro erro comum é usar a avaliação de pré estado para condições prévias.No exemplo, _Requires_lock_held_ é uma condição anterior.

// 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);

A expressão result se refere a um valor de pré estado que não está disponível em passos estado.

RECTIFIQUE no _Success_

Se a função é bem-sucedido quando o valor de retorno é diferente de zero, use return != 0 como a condição com êxito em vez de return == TRUE.Diferente de zero não significa necessariamente equivalência para o valor real que fornece o compilador para TRUE.O parâmetro a _Success_ é uma expressão, e as seguintes expressões são avaliadas como equivalentes: return != 0, return != false, return != FALSE, e return sem parâmetros ou comparações.

// 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
);

Variável de referência

Para uma variável de referência, a versão anterior de SAL usou o ponteiro inferido como o destino de anotação e necessária a adição de __deref as anotações que anexaram a uma variável de referência.Esta versão usa o próprio objeto e não requer _Deref_adicional.

// 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
);

Anotações em valores de retorno

O exemplo a seguir mostra as anotações de um valor de retorno problema comum em.

// Incorrect
_Out_opt_ void *MightReturnNullPtr1();

// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();

Nesse exemplo, _Out_opt_ informa que o ponteiro pode ser NULO como parte da condição anterior.No entanto, as condições prévias não podem ser aplicadas ao valor de retorno.Nesse caso, a anotação correta é _Ret_maybenull_.

Consulte também

Referência

Anotando parâmetros de função e valores de retorno

Anotando o comportamento da função

Anotando estruturas e classes

Anotando o comportamento de bloqueio

Especificando quando e onde uma anotação se aplica

Funções intrínsecas

Conceitos

Noções básicas sobre SAL

Outros recursos

Usando o SAL anotações para reduzir os defeitos no código C/C++