Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Hier volgen enkele manieren om optimaal gebruik te maken van de Broncode Annotation Language (SAL) en enkele veelvoorkomende problemen te voorkomen.
_In_
Als de functie naar het element moet schrijven, gebruik dan _Inout_ in plaats van _In_. Dit is relevant in gevallen van automatische conversie van oudere macro's naar SAL. Vóór SAL gebruikten veel programmeurs macro's als opmerkingen, macro's met de naam IN, OUTof IN_OUTvarianten van deze namen. Hoewel we u aanraden deze macro's te converteren naar SAL, raden we u ook aan om voorzichtig te zijn wanneer u ze converteert, omdat de code mogelijk is gewijzigd sinds het oorspronkelijke prototype is geschreven en de oude macro mogelijk niet meer aangeeft wat de code doet. Wees vooral voorzichtig met de OPTIONAL opmerkingsmacro omdat deze vaak onjuist is geplaatst, bijvoorbeeld aan de verkeerde kant van een komma.
#include <sal.h>
// Incorrect
void Func1(_In_ int *p1)
{
if (p1 == NULL)
return;
*p1 = 1;
}
// Correct
// _Out_opt_ because the function tolerates NULL as a valid argument, i.e.
// no error is returned. If the function didn't check p1 for NULL, then
// _Out_ would be the better choice
void Func2(_Out_opt_ PCHAR p1)
{
if (p1 == NULL)
return;
*p1 = 1;
}
_opt_
Als de aanroeper geen null-aanwijzer mag doorgeven, gebruikt _In_ of _Out_ in plaats van _In_opt_ of _Out_opt_. Dit geldt zelfs voor een functie die de parameters controleert en een fout retourneert als dit niet NULL het geval is. Hoewel het controleren van de parameter van een functie op onverwachte NULL en op de juiste manier teruggeven een goede verdedigende programmeerpraktijk is, betekent dit niet dat de parameterannotatie van een optioneel type (_*Xxx*_opt_) mag zijn.
#include <sal.h>
// Incorrect
void Func1(_Out_opt_ int *p1)
{
*p = 1;
}
// Correct
void Func2(_Out_ int *p1)
{
*p = 1;
}
_Pre_defensive_ en _Post_defensive_
Als een functie op een vertrouwensgrens wordt weergegeven, raden we u aan de _Pre_defensive_ aantekening te gebruiken. De 'defensieve' wijzigingsfunctie wijzigt bepaalde aantekeningen om aan te geven dat de interface op het moment van aanroep strikt moet worden gecontroleerd, maar in de implementatiebody moet worden ervan uitgegaan dat onjuiste parameters kunnen worden doorgegeven. In dat geval wordt _In_ _Pre_defensive_ de voorkeur gegeven aan vertrouwensgrens om aan te geven dat een aanroeper een fout krijgt wanneer geprobeerd wordt NULL door te geven, de functie wordt geanalyseerd alsof de parameter NULL kan zijn, en pogingen om de aanwijzer te derefereren zonder NULL eerst te controleren worden gemarkeerd. Er is ook een _Post_defensive_ aantekening beschikbaar voor gebruik in callbacks waarbij wordt aangenomen dat de vertrouwde partij de aanroeper is en de niet-vertrouwde code de aangeroepen code is.
_Out_writes_
In het volgende voorbeeld ziet u een veelvoorkomend misbruik van _Out_writes_.
#include <sal.h>
// Incorrect
void Func1(_Out_writes_(size) CHAR *pb,
DWORD size
);
De aantekening _Out_writes_ geeft aan dat u een buffer hebt. Er zijn cb bytes toegewezen, waarbij de eerste byte bij het afsluiten is geïnitialiseerd. Deze aantekening is niet strikt onjuist en het is handig om de toegewezen grootte uit te drukken. Er wordt echter niet aangegeven hoeveel elementen de functie initialiseert.
In het volgende voorbeeld ziet u drie juiste manieren om de exacte grootte van het geïnitialiseerde gedeelte van de buffer volledig op te geven.
#include <sal.h>
// 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
Het gebruik van _Out_ PSTR is bijna altijd verkeerd. Deze combinatie wordt geïnterpreteerd als een uitvoerparameter die verwijst naar een tekenbuffer en de buffer is met een null-teken beëindigd.
#include <sal.h>
// Incorrect
void Func1(_Out_ PSTR pFileName, size_t n);
// Correct
void Func2(_Out_writes_(n) PSTR wszFileName, size_t n);
Een aantekening zoals _In_ PCSTR is gebruikelijk en nuttig. Het verwijst naar een invoertekenreeks die null-beëindiging heeft omdat de voorwaarde voor _In_ toestaat dat een null-beëindigde tekenreeks herkend kan worden.
_In_ WCHAR* p
_In_ WCHAR* p geeft aan dat er een invoeraanwijzer p is die naar één teken wijst. In de meeste gevallen is dit echter waarschijnlijk niet de specificatie die bedoeld is. In plaats daarvan is wat waarschijnlijk bedoeld is de specificatie van een null-beëindigde matrix; om dat te doen, gebruikt _In_ PWSTRu .
#include <sal.h>
// Incorrect
void Func1(_In_ WCHAR* wszFileName);
// Correct
void Func2(_In_ PWSTR wszFileName);
Het ontbreken van een juiste specificatie van null-beëindiging is gebruikelijk. Gebruik de juiste STR versie om het type te vervangen, zoals wordt weergegeven in het volgende voorbeeld.
#include <sal.h>
#include <string.h>
// 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_
Als de parameter een aanwijzer is en u het bereik wilt uitdrukken van de waarde van het element waarnaar de aanwijzer verwijst, gebruikt _Deref_out_range_ u in plaats van _Out_range_. In het volgende voorbeeld wordt het bereik van *pcbFilled uitgedrukt, niet pcbFilled.
#include <sal.h>
// 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) is niet strikt vereist voor sommige hulpprogramma's omdat het kan worden afgeleid van _Out_writes_to_(cbSize,*pcbFilled), maar deze wordt hier weergegeven voor volledigheid.
Verkeerde context in _When_
Een andere veelvoorkomende fout is het gebruik van post-statusevaluatie voor precondities. In het volgende voorbeeld _Requires_lock_held_ is dit een voorwaarde.
#include <sal.h>
// 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);
De expressie return verwijst naar een poststatuswaarde die niet beschikbaar is in de pre-status.
TRUE in _Success_
Als de functie slaagt wanneer de retourwaarde niet nul is, gebruikt return != 0 u deze als geslaagde voorwaarde in plaats van return == TRUE. Nonzero betekent niet noodzakelijkerwijs gelijkwaardigheid aan de werkelijke waarde die de compiler biedt TRUE. De parameter van _Success_ is een expressie, en de volgende expressies worden geëvalueerd als gelijkwaardig: return != 0, return != false, return != FALSE en return zonder andere parameters of vergelijkingen.
// 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
);
Verwijzingsvariabele
Voor een referentievariabele gebruikte de vorige versie van SAL de impliciete aanwijzer als het annotatiedoel en vereiste dat er een __deref werd toegevoegd aan annotaties die aan een referentievariabele zijn gekoppeld. Deze versie maakt gebruik van het object zelf en vereist geen _Deref_.
#include <sal.h>
// 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
);
Aantekeningen bij retourwaarden
In het volgende voorbeeld ziet u een veelvoorkomend probleem bij aantekeningen van retourwaarden.
#include <sal.h>
// Incorrect
_Out_opt_ void *MightReturnNullPtr1();
// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();
In dit voorbeeld _Out_opt_ wordt aangegeven dat de aanwijzer mogelijk NULL deel uitmaakt van de voorwaarde. Voorwaarden kunnen echter niet worden toegepast op de retourwaarde. In dit geval is _Ret_maybenull_ de juiste annotatie.
Zie ook
SAL-aantekeningen gebruiken om C/C++-codefouten te verminderen
Informatie over SAL
Annoteren van functieparameters en teruggeven van waarden
Gedrag van functies annoteren
Aantekeningen toevoegen aan structs en klassen
Het annoteren van vergrendelgedrag
Opgeven wanneer en waar een aantekening van toepassing is
Intrinsieke functies