Kodėl senas WinINet tarpinio serverio pagalbininkas lūžta 64 bitų kompiliacijoje
Atpažinkite rodyklių nukirpimą C# tarpinio serverio interop pavyzdžiuose ir prieš naudodami juos dar kartą peržiūrėkite struktūrų išdėstymą ir atminties nuosavybę.
Vienas cast gali paversti iš pažiūros įprastą tarpinio serverio nustatymų pagalbininką nuo architektūros priklausančia klaida:
current = (IntPtr)((int)current + optionSize);
Šis kodas susiaurina vietinį adresą iki 32 bitų sveikojo skaičiaus prieš pridėdamas kitos struktūros dydį. 64 bitų procesas gali laikyti adresus, kurie netelpa. Priklausomai nuo konvertavimo ir reikšmės, rezultatas gali būti perpildymo išimtis arba neteisingas adresas. Kompiliacijos grąžinimas į x86 gali paslėpti klaidą, bet palieka interop įgyvendinimą priklausomą nuo to apeinamo sprendimo.
Laikykite adresus rodyklės dydžio forma. Pavyzdžiui, judėjimui per jau paskirstytą masyvą galima naudoti:
IntPtr item = IntPtr.Add(buffer, checked(index * optionSize));
Poslinkis lieka sveikuoju skaičiumi; pats adresas nėra išspaudžiamas per jį. Paskirstymo dydis ir indeksas vis tiek turi būti galiojantys.
Tada patikrinkite aplinkines deklaracijas
WinINet parinktys kiekvienam ryšiui apima sąjungą, kurios nariai yra DWORD, eilutės rodyklė ir FILETIME. Rodyklės laukams reikia IntPtr; DWORD laukai lieka 32 bitų. Vietinis lygiavimas svarbus, todėl kiekvieno sveikojo skaičiaus pakeitimas 64 bitų sveikuoju skaičiumi arba supakuoto išdėstymo primetimas nėra tinkamas taisymas.
Naudokite struktūros dydį po marshaling, kai einate per masyvą, nuosekliai suderinkite ANSI arba Unicode deklaraciją ir sekite kiekvieną paskirstymą atskirai. Parinkčių struktūrų bloko atlaisvinimas automatiškai neatlaisvina kitur paskirstytų eilučių, į kurias tos struktūros nurodo.
Galiausiai atskirkite nustatymų apimties klausimą nuo atminties teisingumo. Cast sutaisymas neįrodo, kad pagalbininko pasirinkta WinINet parinktis veikia tik vieną valdiklį, vieną procesą ar dabartinio naudotojo nustatymus. Patikrinkite tą API elgseną prieš priimdami likusią seno pavyzdžio dalį. Jei jis taip pat siunčia atnaujinimo pranešimus, pasirinkite juos numatytai apimčiai.