Czy ktoś mógłby wyjaśnić, co to jest
__imp__fprintf
i
__imp____iob_func
nierozwiązane środki zewnętrzne?
Ponieważ otrzymuję te błędy, gdy próbuję skompilować:
1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _ShowError
1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp____iob_func referenced in function _ShowError
1>E:\Documents\Visual Studio 2015\Projects\SDL2_Test\Debug\SDL2_Test.exe : fatal error LNK1120: 2 unresolved externals
Mogę już powiedzieć, że problem nie polega na błędnym linkowaniu. Podłączyłem wszystko poprawnie, ale z jakiegoś powodu nie można się skompilować.
Próbuję użyć SDL2.
Używam programu Visual Studio 2015 jako kompilatora.
Połączyłem się z SDL2.lib i SDL2main.lib w Linker -> Input -> Additional Dependencies i upewniłem się, że katalogi VC ++ są poprawne.
c++
sdl-2
visual-studio-2015
unresolved-external
RockFrenzy
źródło
źródło
Odpowiedzi:
W końcu zrozumiałem, dlaczego tak się dzieje!
W programie Visual Studio 2015 stdin, stderr i stdout są zdefiniowane w następujący sposób:
Ale wcześniej były one definiowane jako:
Dlatego teraz __iob_func nie jest już zdefiniowane, co prowadzi do błędu łącza podczas korzystania z pliku .lib skompilowanego z poprzednimi wersjami programu Visual Studio.
Aby rozwiązać ten problem, możesz spróbować zdefiniować
__iob_func()
siebie, który powinien zwrócić tablicę zawierającą{*stdin,*stdout,*stderr}
.Jeśli chodzi o inne błędy linków dotyczące funkcji stdio (w moim przypadku tak było
sprintf()
), możesz dodać legacy_stdio_definitions.lib do opcji konsolidatora.źródło
extern "C"
w deklaracji / definicji.extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; }
Dla Milana Babuškova, IMO, dokładnie tak powinna wyglądać funkcja zamiany :-)
źródło
#if defined(_MSC_VER) && (_MSC_VER >= 1900)
.Microsoft ma specjalną uwagę na ten temat ( https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT ):
źródło
#pragma comment(lib, "legacy_stdio_definitions.lib")
- ale to nie rozwiązuje problemu__imp___iob_func
- czy jest do tego również starsza biblioteka?Jak odpowiedziałem powyżej, właściwą odpowiedzią jest skompilowanie wszystkiego z VS2015, ale dla zainteresowania poniżej znajduje się moja analiza problemu.
Wydaje się, że ten symbol nie jest zdefiniowany w żadnej statycznej bibliotece dostarczonej przez Microsoft w ramach VS2015, co jest dość osobliwe, ponieważ wszystkie inne są. Aby dowiedzieć się, dlaczego, musimy przyjrzeć się deklaracji tej funkcji i, co ważniejsze, jak jest używana.
Oto fragment z nagłówków programu Visual Studio 2008:
Widzimy więc, że zadaniem funkcji jest zwrócenie początku tablicy obiektów PLIKU (nie uchwytów, „PLIK *” jest uchwytem, PLIK jest podstawową nieprzezroczystą strukturą danych przechowującą ważne elementy stanu). Użytkownikami tej funkcji są trzy makra stdin, stdout i stderr, które są używane do różnych wywołań w stylu fscanf, fprintf.
Przyjrzyjmy się teraz, jak Visual Studio 2015 definiuje te same rzeczy:
Tak więc podejście zmieniło się, aby funkcja zastępująca zwracała teraz uchwyt pliku, a nie adres tablicy obiektów plików, a makra zmieniły się, aby po prostu wywołać funkcję przekazującą numer identyfikacyjny.
Dlaczego więc nie mogą / my zapewnić kompatybilnego interfejsu API? Istnieją dwie kluczowe zasady, których Microsoft nie może naruszać, jeśli chodzi o ich pierwotną implementację za pośrednictwem __iob_func:
Jakakolwiek zmiana w jednym z powyższych oznaczałaby istniejący skompilowany kod powiązany z tym, który poszedłby źle, gdyby wywołano ten interfejs API.
Przyjrzyjmy się, jak zdefiniowano / jest zdefiniowany PLIK.
Najpierw definicja pliku VS2008:
A teraz definicja pliku VS2015:
A więc sedno tego: struktura zmieniła kształt. Istniejący skompilowany kod odwołujący się do __iob_func opiera się na fakcie, że zwracane dane są zarówno tablicą, która może być indeksowana, jak i że w tej tablicy elementy są w tej samej odległości od siebie.
Możliwe rozwiązania wymienione w powyższych odpowiedziach nie zadziałałyby (gdyby zostały wywołane) z kilku powodów:
Tablica FILE _iob zostanie skompilowana za pomocą VS2015, a więc zostanie ułożona jako blok struktur zawierających void *. Zakładając wyrównanie 32-bitowe, elementy te byłyby oddalone od siebie o 4 bajty. Więc _iob [0] jest na offset 0, _iob [1] na 4, a _iob [2] na offset 8. Zamiast tego kod wywołujący oczekuje, że PLIK będzie znacznie dłuższy, wyrównany do 32 bajtów w moim systemie, i tak weźmie adres zwróconej tablicy i doda 0 bajtów, aby dostać się do elementu zerowego (ten jest w porządku), ale dla _iob [1] wywnioskuje, że musi dodać 32 bajty, a dla _iob [2] wydedukuje że musi dodać 64 bajty (bo tak to wyglądało w nagłówkach VS2008). I rzeczywiście zdemontowany kod VS2008 to pokazuje.
Drugim problemem związanym z powyższym rozwiązaniem jest kopiowanie zawartości struktury FILE (* stdin), a nie uchwytu FILE *. Tak więc każdy kod VS2008 szukałby innej podstawowej struktury niż VS2015. Może to zadziałać, jeśli struktura zawiera tylko wskaźniki, ale to duże ryzyko. W każdym razie pierwsza kwestia czyni to nieistotnym.
Jedyny hack, jaki udało mi się wymyślić, to taki, w którym __iob_func chodzi po stosie wywołań, aby dowiedzieć się, którego rzeczywistego uchwytu pliku szuka (na podstawie przesunięcia dodanego do zwróconego adresu) i zwraca obliczoną wartość taką, że daje właściwą odpowiedź. To jest tak samo szalone, jak się wydaje, ale prototyp tylko dla x86 (nie x64) jest wymieniony poniżej dla twojej rozrywki. W moich eksperymentach zadziałało dobrze, ale przebieg może się różnić - niezalecany do użytku produkcyjnego!
źródło
Miałem ten sam problem w VS2015. Rozwiązałem to, kompilując źródła SDL2 w VS2015.
źródło
SDL2main
i skopiowaćSDL2main.lib
Nie wiem dlaczego, ale:
Po dołączeniach, ale przed głównym powinien to naprawić z mojego doświadczenia.
źródło
Nowsze rozwiązanie tego problemu: użyj nowszych bibliotek sdl na platformie
„ https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/?C=M;O=D ”
Wydaje się, że naprawili problem, chociaż jest to tylko biblioteka 32-bitowa (tak mi się wydaje).
źródło
Łączenie oznacza nieprawidłowe działanie. Zagłębiając się w stdio.h VS2012 i VS2015, zadziałało dla mnie następujące. Niestety, musisz zdecydować, czy powinno działać dla jednego z {stdin, stdout, stderr}, nigdy więcej niż jednego.
źródło
Moja rada jest taka, aby nie (próbować) wdrażać __iob_func.
Podczas naprawiania tych błędów:
libpngd.v110.lib(pngrutil.obj) : error LNK2001: unresolved external symbol ___iob_func curllib.v110.lib(mprintf.obj) : error LNK2001: unresolved external symbol ___iob_func
Wypróbowałem rozwiązania innych odpowiedzi, ale w końcu zwrócenie
FILE*
tablicy C nie pasuje do tablicy wewnętrznych struktur IOB systemu Windows. @Volker ma rację, że to nigdy nie będziesz pracować dłużej niż jednejstdin
,stdout
lubstderr
.Jeśli biblioteka faktycznie WYKORZYSTUJE jeden z tych strumieni, ulegnie awarii . Dopóki Twój program nie spowoduje, że biblioteka ich użyje, nigdy się nie dowiesz . Na przykład
png_default_error
zapisuje,stderr
gdy CRC nie pasuje w metadanych PNG. (Zwykle nie jest to problem warty awarii)Wniosek: nie jest możliwe mieszanie bibliotek VS2012 (Platform Toolset v110 / v110_xp) i VS2015 +, jeśli używają one stdin, stdout i / lub stderr.
Rozwiązanie: Ponownie skompiluj biblioteki, które mają
__iob_func
nierozwiązane symbole, z bieżącą wersją VS i pasującym zestawem narzędzi platformy.źródło
Użyj wstępnie skompilowanych SDL2main.lib i SDL.lib dla biblioteki swojego projektu VS2015: https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225.zip
źródło
Rozwiązuję ten problem za pomocą następującej funkcji. Używam Visual Studio 2019.
ponieważ stdin Wywołanie funkcji zdefiniowanej przez makro, wyrażenie "* stdin" nie jest używane globalny inicjator tablicy. Ale możliwa jest inicjalizacja tablicy lokalnej. przepraszam, jestem słaby z angielskiego.
źródło
Dla każdego, kto wciąż szuka odpowiedzi, gdzie powyższe sztuczki nie zadziałały. Łączenie statyczne jest sposobem na rozwiązanie tego problemu. Zmień ustawienia biblioteki Runtime, jak poniżej
Project properties --> C/C++ --> Code generation --> Runtime Library --> Multi-threaded Debug (/MTd) instead of /MDd
źródło
Udało mi się rozwiązać problem.
Źródłem błędu był ten wiersz kodu, który można znaleźć w kodzie źródłowym SDLmain.
Więc co zrobiłem, to edytować kod źródłowy w SDLmain również tej linii:
Następnie zbudowałem SDLmain i skopiowałem i zastąpiłem stary SDLmain.lib w katalogu mojej biblioteki SDL2 nowo zbudowanym i edytowanym.
Następnie, gdy uruchomiłem program z SDL2, nie pojawiły się żadne komunikaty o błędach, a kod działał płynnie.
Nie wiem, czy to mnie później ugryzie, ale wszystko idzie świetnie.
źródło
Może się to zdarzyć, gdy łączysz się z msvcrt.dll zamiast msvcr10.dll (lub podobnym), co jest dobrym planem. Ponieważ pozwoli Ci to na redystrybucję biblioteki środowiska uruchomieniowego programu Visual Studio w końcowym pakiecie oprogramowania.
To obejście pomaga mi (w programie Visual Studio 2008):
Ten fragment nie jest potrzebny w przypadku programu Visual Studio 6 i jego kompilatora. Dlatego #ifdef.
źródło
Aby wprowadzić więcej zamieszania w tym już bogatym wątku, zdarzyło mi się wpaść na ten sam nierozwiązany zewnętrzny na fprintf
Nawet jeśli w moim przypadku było to w zupełnie innym kontekście: pod Visual Studio 2005 (Visual Studio 8.0) i błąd występował w moim własnym kodzie (tym samym, który kompilowałem), a nie strony trzeciej.
Zdarzyło się, że ten błąd został wywołany przez opcję / MD w moich flagach kompilatora. Przełączenie na / MT usunęło problem. To dziwne, ponieważ zazwyczaj łączenie statyczne (MT) stwarza więcej problemów niż dynamiczne (MD) ... ale na wypadek, gdyby służyło innym, umieszczam to tam.
źródło
W moim przypadku ten błąd pochodzi z mojej próby usunięcia zależności od biblioteki DLL zależnej od wersji MSVC (msvcr10.dll lub podobnej) i / lub usunięcia statycznej biblioteki wykonawczej, aby usunąć nadmiar tłuszczu z moich plików wykonywalnych.
Używam więc przełącznika linkera / NODEFAULTLIB, mojego samodzielnie utworzonego „msvcrt-light.lib” (google w razie potrzeby) i
mainCRTStartup()
/WinMainCRTStartup()
wpisów.Jest to IMHO od czasu Visual Studio 2015, więc trzymałem się starszych kompilatorów.
Jednak definiowanie symbolu _NO_CRT_STDIO_INLINE eliminuje wszelkie kłopoty, a prosta aplikacja „Hello World” ma znowu 3 KB i nie jest zależna od nietypowych bibliotek DLL. Przetestowano w programie Visual Studio 2017.
źródło
Wklej ten kod do dowolnego ze swoich plików źródłowych i ponownie skompiluj. Pracował dla mnie!
#include stdio.h
PLIK _iob [3];
PLIK * __cdecl __iob_func (void) {
_iob [0] = * stdin;
_iob [0] = * standardowe wyjście;
_iob [0] = * stderr;
return _iob;
}
źródło