Czy natrafiłeś kiedyś na wyjątek typu BadImageFormatException lub komunikat "An attempt was made to load a program with an incorrect format" ("Próbowano załadować program w niepoprawnym formacie")?
Jeśli tak to być może program, który próbowałeś uruchomić nie został skompilowany z użyciem opcji /platform:x86. Zastanawiasz się pewnie dlaczego podczas programowania w C# powinieneś przejmować się tym na jakiej platformie (x86/x64) kod zostanie wykonany. Cóż, w większości przypadków nie musisz o tym myśleć. Jeśli nie używasz bloków unsafe ani nie importujesz natywnych modułów problemu nie ma, bowiem Twój kod C# zostaje przetłumaczony do kodu pośredniego (CIL), który przed wykonaniem zostaje skompilowany (JIT) na postać odpowiednią dla platformy docelowej. Ok, ale...
Wyobraź sobie, że używasz w swojej aplikacji funkcji importowanej z 32 bitowej DLL. Gdy uruchomisz program na 32 bitowym systemie wszystko działa jak należy. Niestety na maszynie x64 otrzymujesz wspominany wyjątek BadImageFormatException. Dlaczego? Jeśli assembly importujące DLL zostało skompilowane z opcją /platform:anycpu to do jego uruchomienia na systemie 64 bitowym została użyta 64 bitowa wersja CLR. Próba załadowania 32 bitowej DLL z aplikacji działającej w procesie 64 bitowym nie powiedzie się. Gdyby do kompilacji została użyta opcja /platform:x86 wówczas na 64 bitowym systemie operacyjnym program zostałby uruchomiony w 32 bitowej wersji CLR (z użyciem WoW64: Windows 32-bit on Windows 64-bit).
Platformę docelową (przełącznik /platform kompilatora C#) można w Visual Studio 2010 ustawić w oknie “Properties” na zakładce “Build”. By tam trafić kliknij prawym klawiszem w plik projektu w Solution Explorer i wybierz „Properties” lub użyj menu głównego „Project | <nazwa projektu> Properties…”.
Microsoft stworzył przydatne narzędzie wiersza poleceń o nazwie CorFlags, które służy między innymi do podglądu lub ustawiania platformy docelowej. Dostęp do tego narzędzia można uzyskać korzystając z Visual Studio Command Prompt albo przez znalezienie go bezpośrednio na dysku (u mnie jesto ono pod C:\Program Files\Microsoft.NET\SDK\v2.0\Bin\CorFlags.exe)
Poniżej znajduje się kilka przykładów tego co możesz zobaczyć po sprawdzeniu plików EXE stworzonych z różnymi wartościami opcji /platform kompilatora (do sprawdzenia pliku służy polecenie: CorFlags nazwa.pliku):
anycpu |
x86 |
x64 |
Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 1
ILONLY : 1
32BIT : 0
Signed : 0
|
Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 3
ILONLY : 1
32BIT : 1
Signed : 0
|
Version : v4.0.30319
CLR Header: 2.5
PE : PE32+
CorFlags : 1
ILONLY : 1
32BIT : 0
Signed : 0
|
W kontekście tego posta istotne są 2 rzędy z wyniku zwróconego przez CorFlags: PE i 32BIT.
- PE: PE32 oznacza, że plik może być wykonany w środowisku x86 i x64
- PE: PE32+ oznacza, że plik może być wykonany jedynie w środowisku 64 bitowym
- 32BIT: 1 oznacza, że program musi być wykonany w środowisku x86
Zrozumienie znaczenia 32BIT: 1 jest naprawdę istotne jeśli chcesz uniknąć problemów z importowanie 32 bitowch DLL na 64 bitowej wersji Windows. Jeśli flaga 32BIT jest ustawiona i uruchomisz plik PE32 na x64 wówczas Twoja aplikacja zostanie uruchomiona w środowisku 32 bitowym (z użyciem WoW), dzięki czemu zaistnieje możliwość zaimportowania 32 bitowej DLL. Jeśli flaga 32BIT nie jest ustawiona, aplikacja uruchomi się w procesie 64 bitowym – co spowoduje problem z załadowaniem biblioteki.
Dzięki CorFlags można w łatwy sposób zmodyfikować wartość flagi 32BIT. Do jej ustawienia służy przełącznik /32BIT+
CorFlags file.exe /32BIT+
A do jej usuwania /32BIT-
CorFlags file.exe /32BIT-
Tak więc nawet jeśli nie masz możliwości przekompilowania problematycznego kodu z odpowiednią opcją /platform nadal możesz użyć 32 bitowej DLL w 64 bitowej wersji systemu Windows :)