Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Obecné
Používejte nové 64bitově bezpečné datové typy Windows.
Nové 64bitové datové typy bezpečné, popsané výše v tomto dokumentu, jsou definovány v Basetsd.h. Tento hlavičkový soubor je součástí ntdef.h, který je součástí Ntddk.h, Wdm.h a Ntifs.h.
Makra kompilátoru platformy používejte pečlivě.
Následující předpoklad už není platný:
#ifdef _WIN32 // 32-bit Windows code ... #else // 16-bit Windows code ... #endif
64bitový kompilátor však definuje _WIN32 pro zpětnou kompatibilitu.
Také následující předpoklad už není platný:
#ifdef _WIN16 // 16-bit Windows code ... #else // 32-bit Windows code ... #endif
V tomto případě může klauzule else představovat _WIN32 nebo _WIN64.
Použijte správné formátovací specifikátory s printf a wsprintf.
K tisku ukazatelů v šestnáctkovém formátu použijte %p. To je nejlepší volba pro tisk ukazatelů.
Poznámka Budoucí verze visual C++ bude podporovat %I k tisku polymorfních dat. Bude považovat hodnoty za 64 bitů v 64bitovém systému Windows a 32 bitů v 32bitovém systému Windows. Visual C++ bude také podporovat %I64 k tisku hodnot, které jsou 64 bitů.
Znáte svůj adresní prostor.
Slepě nepředpokládejme, že pokud je adresa jádra, musí být nastaven její bit s vysokým pořadím. Pokud chcete získat nejnižší systémovou adresu, použijte makro MM_LOWEST_SYSTEM_ADDRESS.
Aritmetika s ukazateli
Při provádění nepodepsaných a podepsaných operací buďte opatrní.
Vezměte v úvahu následující skutečnosti:
ULONG x; LONG y; LONG *pVar1; LONG *pVar2; pVar2 = pVar1 + y * (x - 1);
K tomuto problému dochází, protože x je bez znaménka, což činí celý výraz také bez znaménka. To funguje správně, pokud y není negativní. V tomto případě se y převede na nepodepsanou hodnotu, výraz se vyhodnotí pomocí 32bitové přesnosti, škáluje a přidá do pVar1. V 64bitovém systému Windows se toto 32bitové záporné číslo bez znaménka změní na velké 64bitové kladné číslo, které vrátí nesprávný výsledek. Chcete-li tento problém vyřešit, deklarujte x jako podepsanou hodnotu nebo explicitně zadejte ji do LONG ve výrazu.
Při použití šestnáctkových konstant a nepodepsaných hodnot buďte opatrní.
Následující tvrzení není na 64bitových systémech pravdivé:
~((UINT64)(PAGE_SIZE-1)) == (UINT64)~(PAGE_SIZE-1) PAGE_SIZE = 0x1000UL // Unsigned long - 32 bits PAGE_SIZE - 1 = 0x00000fff
Výraz LHS:
// Unsigned expansion(UINT64)(PAGE_SIZE -1 ) = 0x0000000000000fff ~((UINT64)(PAGE_SIZE -1 )) = 0xfffffffffffff000
Výraz RHS:
~(PAGE_SIZE-1) = 0xfffff000 (UINT64)(~(PAGE_SIZE - 1)) = 0x00000000fffff000
Proto:
~((UINT64)(PAGE_SIZE-1)) != (UINT64)(~(PAGE_SIZE-1))
Buďte opatrní při operacích NOT.
Vezměte v úvahu následující skutečnosti:
UINT_PTR a; ULONG b; a = a & ~(b - 1);
Problémem je, že ~(b−1) vytváří 0x0000 0000 xxxx xxxx a ne 0xFFFF FFFF xxxx. Kompilátor to nezjistí. Pokud chcete tento problém vyřešit, změňte kód následujícím způsobem:
a = a & ~((UINT_PTR)b - 1);
Při výpočtech velikostí vyrovnávací paměti buďte opatrní.
Vezměte v úvahu následující skutečnosti:
len = ptr2 - ptr1 /* len could be greater than 2**32 */
Přetypovat ukazatele na PCHAR pro aritmetiku ukazatelů.
Poznámka Pokud je deklarován INT nebo ULONG, vygeneruje se upozornění kompilátoru. Velikosti vyrovnávací paměti, i když jsou správně vypočteny, mohou stále překročit kapacitu ULONG.
Nepoužívejte vypočítané nebo pevně zakódované posuny ukazatelů.
Při práci se strukturami použijte makro FIELD_OFFSET tam, kde je to možné, určit posun členů struktury.
Nepoužívejte pevně zakódované hodnoty ukazatele nebo popisovače.
Nepředávejte pevně zakódované ukazatele ani úchyty, jako je (HANDLE) 0xFFFFFFFF rutinám, jako je ZwCreateSection. Místo toho použijte konstanty, například INVALID_HANDLE_VALUE, které lze definovat tak, aby měly odpovídající hodnotu pro každou platformu.
Mějte na paměti, že v 64bitovém systému Windows není 0xFFFFFFFF stejné jako -1.
Například:
DWORD index = 0; CHAR *p; // if (p[index-1] == '0') causes access violation on 64-bit Windows!
Na 32bitových počítačích:
p[index-1] == p[0xffffffff] == p[-1]
Na 64bitových počítačích:
p[index-1] == p[0x00000000ffffffff] != p[-1]
Tento problém lze vyhnout změnou typu indexu z DWORD na DWORD_PTR.
Polymorfismus
Buďte opatrní s polymorfními rozhraními.
Nevytvořte funkce, které přijímají parametry typu DWORD (nebo jiné typy s pevnou přesností) pro polymorfní data. Pokud mohou být data ukazatelem nebo celočíselnou hodnotou, typ parametru by měl být UINT_PTR nebo PVOID, nikoli DWORD.
Například nevytvořte funkci, která přijímá pole parametrů výjimky zadaných jako hodnoty DWORD. Pole má být pole hodnot DWORD_PTR. Prvky pole proto mohou obsahovat adresy nebo 32bitové celočíselné hodnoty. Obecné pravidlo je, že pokud je původní typ DWORD a musí být šířka ukazatele, převeďte ho na hodnotu DWORD_PTR. Proto existují odpovídající typy přesnosti ukazatele pro nativní typy Win32. Pokud máte kód, který používá DWORD, ULONGnebo jiné 32bitové typy polymorfním způsobem (to znamená, že chcete, aby člen struktury nebo parametru držel adresu), použijte UINT_PTR místo aktuálního typu.
Při volání funkcí s parametry OUT ukazatele buďte opatrní.
Nedělejte to:
void GetBufferAddress(OUT PULONG *ptr); { *ptr=0x1000100010001000; } void foo() { ULONG bufAddress; // // This call causes memory corruption. // GetBufferAddress((PULONG *)&bufAddress); }
Typování bufAddress na (PULONG *) brání chybě při kompilaci. GetBufferAddress však zapíše 64bitovou hodnotu do paměťové adresy na &bufAddress. Protože bufAddress je pouze 32bitová hodnota, 32 bitů bezprostředně po bufAddress se přepíšou. Jedná se o velmi jemnou chybu, kterou je těžké najít.
Nepřetypovávejte ukazatele na INT , LONG , ULONG nebo DWORD .
Pokud je nutné přetypovat ukazatel, aby bylo možné testovat některé bity, nastavit nebo vymazat bity nebo jinak manipulovat s obsahem, použijte UINT_PTR nebo INT_PTR typ. Tyto typy jsou integrální typy, které se škálují na velikost ukazatele pro 32bitovou i 64bitovou verzi Windows (například ULONG pro 32bitovou verzi Windows a _int64 pro 64bitovou verzi Windows). Předpokládejme například, že portujete následující kód:
ImageBase = (PVOID)((ULONG)ImageBase | 1);
V rámci procesu přenosu byste kód změnili následujícím způsobem:
ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);
Použijte UINT_PTR a INT_PTR tam, kde je to vhodné (a pokud si nejste jistí, zda jsou požadovány, neškodí je použít raději). Nepřetypujte ukazatele na typy ULONG, LONG, INT, UINTnebo DWORD.
PoznámkaPOPISOVAČ je definován jako void \, takže přetypování *HANDLE hodnoty na hodnotu ULONG, aby se testovaly, nastavily nebo vymazaly nízké dva bity, je programovací chyba.
Ke zkrácení ukazatelů použijte PtrToLong a PtrToUlong.
Pokud je nutné zkrátit ukazatel na 32bitovou hodnotu, použijte funkci PtrToLong nebo PtrToUlong (definovanou v Basetsd.h). Tato funkce zakáže upozornění na zkrácení ukazatele po dobu trvání volání.
Tyto funkce používejte pečlivě. Po zkrácení proměnné ukazatele pomocí jedné z těchto funkcí nikdy nepřetypujte výsledné LONG nebo ULONG zpět na pointer. Tyto funkce zkrátí horních 32 bitů adresy, které jsou obvykle potřeba pro přístup k paměti původně odkazované ukazatelem. Použití těchto funkcí bez pečlivého zvážení způsobí křehký kód.
Datové struktury a zarovnání struktury
Pečlivě prozkoumejte všechna použití ukazatelů datové struktury.
V těchto oblastech jsou běžné problémy:
- Datové struktury, které jsou uložené na disku nebo se vyměňují s 32bitovými procesy.
- Explicitní a implicitní unie s ukazateli
- Popisovače zabezpečení
Použijte makro FIELD_OFFSET.
Například:
struct xx { DWORD NumberOfPointers; PVOID Pointers[1]; };
Následující přidělení je v 64bitovém systému Windows nesprávné, protože kompilátor vyplní strukturu o další 4 bajty, aby splnil požadavek na zarovnání na 8 bajtů.
malloc(sizeof(DWORD)+100*sizeof(PVOID));
Tady je postup, jak to udělat správně:
malloc(FIELD_OFFSET(struct xx, Pointers) +100*sizeof(PVOID));
Použijte makro TYPE_ALIGNMENT.
Makro TYPE_ALIGNMENT vrátí požadavek na zarovnání pro daný datový typ na aktuální platformě. Například:
TYPE_ALIGNMENT(KFLOATING_SAVE) == 4 on x86, 8 on Itanium TYPE_ALIGNMENT(UCHAR) == 1 everywhere
Například kód, například:
ProbeForRead(UserBuffer, UserBufferLength, sizeof(ULONG));
stane se přenosnější, když se změní na:
ProbeForRead(UserBuffer, UserBufferLength, TYPE_ALIGNMENT(ULONG));
Sledujte změny datového typu ve veřejných strukturách jádra.
Například pole Informace ve struktuře IO_STATUS_BLOCK je nyní typu ULONG_PTR.
Při použití direktiv pro balení struktury buďte opatrní.
Pokud je datová struktura v 64bitovém systému Windows nesprávně zarovnaná, rutiny, které manipulují se strukturou, například RtlCopyMemory a memcpy, nebudou vadit. Místo toho vyvolají výjimku. Například:
#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 */ ... }
K opravě můžete použít makro UNALIGNED:
void SetPointer(void *p) { struct Buffer s; *(UNALIGNED void *)&s.ptr = p; }
Použití makra UNALIGNED je bohužel na procesorech s architekturou Itanium velmi nákladné. Lepším řešením je umístit 64bitové hodnoty a ukazatele na začátek struktury.
Poznámka Pokud je to možné, vyhněte se použití různých úrovní balení ve stejném souboru hlaviček.
Další informace
podpora 32bitových vstupně-výstupních operací v 64bitovém ovladači
Příprava na 64bitovou verzi windows (průvodce portováním aplikací v uživatelském režimu)