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 o comportamento de bloqueio
Especificando quando e onde uma anotação se aplica
Conceitos
Outros recursos
Usando o SAL anotações para reduzir os defeitos no código C/C++