Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Allmänt
Använd de nya 64-bitars säkra Windows-datatyperna.
De nya 64-bitars säkra datatyperna, som beskrivs tidigare i det här dokumentet, definieras i Basetsd.h. Den här rubrikfilen ingår i Ntdef.h, som ingår i Ntddk.h, Wdm.h och Ntifs.h.
Använd plattformskompilerarens makron noggrant.
Följande antagande är inte längre giltigt:
#ifdef _WIN32 // 32-bit Windows code ... #else // 16-bit Windows code ... #endif64-bitarskompilatorn definierar dock _WIN32 för bakåtkompatibilitet.
Dessutom är följande antagande inte längre giltigt:
#ifdef _WIN16 // 16-bit Windows code ... #else // 32-bit Windows code ... #endifI det här fallet kan else-satsen representera _WIN32 eller _WIN64.
Använd rätt formatspecificerare med printf och wsprintf.
Använd %p för att skriva ut pekare i hexadecimalt. Det här är det bästa valet för att skriva ut pekare.
Obs En framtida version av Visual C++ kommer att stödja %I för att skriva ut polymorfa data. Värden behandlas som 64 bitar i 64-bitars Windows och 32 bitar i 32-bitars Windows. Visual C++ stöder också %I64 för att skriva ut värden som är 64 bitar.
Känn till din adressutrymme.
Anta till exempel inte blint att om en adress är en kerneladress måste dess högordningsbit anges. Använd MM_LOWEST_SYSTEM_ADDRESS makro för att få den lägsta systemadressen.
Pekare-aritmetik
Var försiktig när du utför osignerade och signerade åtgärder.
Tänk på följande:
ULONG x; LONG y; LONG *pVar1; LONG *pVar2; pVar2 = pVar1 + y * (x - 1);Problemet uppstår eftersom x är osignerat, vilket gör hela uttrycket osignerat. Detta fungerar bra om inte y är negativt. I det här fallet konverteras y till ett osignerat värde, uttrycket utvärderas med 32-bitars precision, skalas och läggs till i pVar1. I 64-bitars Windows blir detta 32-bitars osignerade negativa tal ett stort 64-bitars positivt tal, vilket ger fel resultat. För att åtgärda det här problemet deklarerar du x som ett signerat värde eller skriver det explicit till LONG i uttrycket.
Var försiktig när du använder hexadecimala konstanter och osignerade värden.
Följande försäkran gäller inte för 64-bitarssystem:
~((UINT64)(PAGE_SIZE-1)) == (UINT64)~(PAGE_SIZE-1) PAGE_SIZE = 0x1000UL // Unsigned long - 32 bits PAGE_SIZE - 1 = 0x00000fffLHS-uttryck:
// Unsigned expansion(UINT64)(PAGE_SIZE -1 ) = 0x0000000000000fff ~((UINT64)(PAGE_SIZE -1 )) = 0xfffffffffffff000RHS-uttryck:
~(PAGE_SIZE-1) = 0xfffff000 (UINT64)(~(PAGE_SIZE - 1)) = 0x00000000fffff000Därför:
~((UINT64)(PAGE_SIZE-1)) != (UINT64)(~(PAGE_SIZE-1))Var försiktig med NOT-åtgärder.
Tänk på följande:
UINT_PTR a; ULONG b; a = a & ~(b - 1);Problemet är att ~(b−1) producerar 0x0000 0000 xxxx xxxx och inte 0xFFFF FFFF xxxx xxxx. Kompilatorn identifierar inte detta. Åtgärda detta genom att ändra koden på följande sätt:
a = a & ~((UINT_PTR)b - 1);Var försiktig när du beräknar buffertstorlekar.
Tänk på följande:
len = ptr2 - ptr1 /* len could be greater than 2**32 */Kasta pekare till PCHAR för pekararitmetik.
Observera Om len deklareras som INT eller ULONG genereras en kompilatorvarning. Buffertstorlekar, även om de beräknas korrekt, kan fortfarande överskrida kapaciteten för ULONG.
Undvik att använda beräknade eller hårdkodade pekarförskjutningar.
När du arbetar med strukturer, använd makrot FIELD_OFFSET där det är möjligt för att bestämma förskjutningen av strukturmedlemmar.
Undvik att använda hårdkodade pekare eller handtagsvärden.
Skicka inte hårdkodade pekare eller referenser som (HANDLE)0xFFFFFFFF till rutiner som ZwCreateSection. Använd i stället konstanter, till exempel INVALID_HANDLE_VALUE, som kan definieras för att ha rätt värde för varje plattform.
Tänk på att i 64-bitars Windows är 0xFFFFFFFF inte samma som -1.
Till exempel:
DWORD index = 0; CHAR *p; // if (p[index-1] == '0') causes access violation on 64-bit Windows!På 32-bitars datorer:
p[index-1] == p[0xffffffff] == p[-1]På 64-bitars datorer:
p[index-1] == p[0x00000000ffffffff] != p[-1]Det här problemet kan undvikas genom att ändra typen av index från DWORD till DWORD_PTR.
Polymorfism
Var försiktig med polymorfa gränssnitt.
Skapa inte funktioner som accepterar parametrar av typen DWORD (eller andra typer med fast precision) för polymorfa data. Om data kan vara en pekare eller ett integralvärde ska parametertypen vara UINT_PTR eller PVOID, inte DWORD.
Skapa till exempel inte en funktion som accepterar en matris med undantagsparametrar som anges som DWORD- värden. Matrisen ska vara en matris med DWORD_PTR värden. Därför kan matriselementen innehålla adresser eller 32-bitars integralvärden. Den allmänna regeln är att om den ursprungliga typen är DWORD och den måste vara pekarbredd konverterar du den till ett DWORD_PTR värde. Det är därför det finns motsvarande typer av pekarprecision för de inbyggda Win32-typerna. Om du har kod som använder DWORD, ULONG eller andra 32-bitarstyper på ett polymorft sätt (dvs. du verkligen vill att parametern eller strukturmedlemmen ska innehålla en adress), använder du UINT_PTR i stället för den aktuella typen.
Var försiktig när du anropar funktioner som har OUT-pekarparametrar.
Gör inte så här:
void GetBufferAddress(OUT PULONG *ptr); { *ptr=0x1000100010001000; } void foo() { ULONG bufAddress; // // This call causes memory corruption. // GetBufferAddress((PULONG *)&bufAddress); }Typecasting bufAddress to (PULONG *) förhindrar ett kompilatorfel. GetBufferAddress skriver dock ett 64-bitars värde till minnesplatsen på &bufAddress. Eftersom bufAddress bara är ett 32-bitarsvärde skrivs 32-bitars direkt efter bufAddress över. Detta är en mycket subtil, svår att hitta bugg.
Kasta inte pekare till INT, LONG, ULONG eller DWORD.
Om du måste kasta en pekare för att testa vissa bitar, ange eller rensa bitar eller på annat sätt ändra innehållet använder du typen UINT_PTR eller INT_PTR . Dessa typer är integrerade typer som skalas till storleken på en pekare för både 32-bitars och 64-bitars Windows (till exempel ULONG för 32-bitars Windows och _int64 för 64-bitars Windows). Anta till exempel att du porterar följande kod:
ImageBase = (PVOID)((ULONG)ImageBase | 1);Som en del av portningsprocessen ändrar du koden enligt följande:
ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);Använd UINT_PTR och INT_PTR när det är lämpligt (och om du är osäker på om de krävs, finns det ingen skada i att använda dem bara för det fallet). Lägg inte pekarna till typerna ULONG, LONG, INT, UINTeller DWORD.
NoteHANDLE definieras som ett tomrum \, så att typecasta ett *HANDLE-värde till ett ULONG-värde för att testa, ange eller rensa de låga två bitarna är ett programmeringsfel.
Använd PtrToLong och PtrToUlong för att trunkera pekare.
Om du måste trunkera en pekare till ett 32-bitarsvärde använder du funktionen PtrToLong eller PtrToUlong (definierad i Basetsd.h). Den här funktionen inaktiverar pekarens trunkeringsvarning under hela anropet.
Använd dessa funktioner noggrant. När du har trunkerat en pekarvariabel med någon av dessa funktioner kan du aldrig kasta den resulterande LONG eller ULONG tillbaka till en pekare. Dessa funktioner trunkerar de övre 32 bitarna av en adress, som vanligtvis behövs för att komma åt minnet som ursprungligen refererades av pekaren. Om du använder dessa funktioner utan noggrant övervägande resulterar det i bräcklig kod.
Datastrukturer och strukturjustering
Granska noggrant all användning av datastrukturpekare.
Följande är vanliga problemområden:
- Datastrukturer som lagras på disken eller utbyts med 32-bitarsprocesser.
- Explicita och implicita fackföreningar med pekare.
- Säkerhetsbeskrivningar.
Använd makrot FIELD_OFFSET .
Till exempel:
struct xx { DWORD NumberOfPointers; PVOID Pointers[1]; };Följande allokering är felaktig i 64-bitars Windows eftersom kompilatorn fyller strukturen med ytterligare 4 byte för att göra justeringskravet på 8 byte:
malloc(sizeof(DWORD)+100*sizeof(PVOID));Så här gör du på rätt sätt:
malloc(FIELD_OFFSET(struct xx, Pointers) +100*sizeof(PVOID));Använd makrot TYPE_ALIGNMENT .
Makrot TYPE_ALIGNMENT returnerar justeringskravet för en viss datatyp på den aktuella plattformen. Till exempel:
TYPE_ALIGNMENT(KFLOATING_SAVE) == 4 on x86, 8 on Itanium TYPE_ALIGNMENT(UCHAR) == 1 everywhereTill exempel kod som den här:
ProbeForRead(UserBuffer, UserBufferLength, sizeof(ULONG));blir mer portabel när den ändras till:
ProbeForRead(UserBuffer, UserBufferLength, TYPE_ALIGNMENT(ULONG));Håll utkik efter datatypsändringar i offentliga kernelstrukturer.
Till exempel är fältet Information i IO_STATUS_BLOCK struktur nu av typen ULONG_PTR.
Var försiktig när du använder förpackningsdirektiv för strukturer.
Om en datastruktur är feljusterad i 64-bitars Windows kommer rutiner som manipulerar strukturen, till exempel RtlCopyMemory och memcpy, inte att fungera. I stället skapar de ett undantag. Till exempel:
#pragma pack (1) /* also set by /Zp switch */ struct Buffer { ULONG size; void *ptr; }; void SetPointer(void *p) { struct Buffer s; s.ptr = p; /* will cause alignment fault */ ... }Du kan använda UNALIGNED-makrot för att åtgärda detta:
void SetPointer(void *p) { struct Buffer s; *(UNALIGNED void *)&s.ptr = p; }Tyvärr är det mycket dyrt att använda UNALIGNED-makrot på Itanium-baserade processorer. En bättre lösning är att placera 64-bitars värden och pekare i början av strukturen.
Not Undvik om möjligt att använda olika förpackningsnivåer i samma rubrikfil.
Ytterligare information
Gör dig redo för 64-bitars Windows (portningsguide för program i användarläge)