sdl2 libreria library biblioteca c++ sdl-2 visual-studio-2015 unresolved-external

c++ - libreria - símbolo externo sin resolver__imp__fprintf y__imp____iob_func, SDL2



sdl2 2.0 8 (16)

¡Finalmente he descubierto por qué sucede esto!

En Visual Studio 2015, stdin, stderr, stdout se definen de la siguiente manera:

#define stdin (__acrt_iob_func(0)) #define stdout (__acrt_iob_func(1)) #define stderr (__acrt_iob_func(2))

Pero anteriormente, se definían como:

#define stdin (&__iob_func()[0]) #define stdout (&__iob_func()[1]) #define stderr (&__iob_func()[2])

Entonces ahora __iob_func ya no está definido, lo que conduce a un error de enlace al usar un archivo .lib compilado con versiones anteriores de Visual Studio.

Para resolver el problema, puede intentar definir __iob_func() usted mismo que debería devolver una matriz que contenga {*stdin,*stdout,*stderr} .

Con respecto a los otros errores de enlace sobre las funciones stdio (en mi caso fue sprintf() ), puede agregar legacy_stdio_definitions.lib a sus opciones de enlazador.

¿Podría alguien explicarme qué

__imp__fprintf

y

__imp____iob_func

medios externos no resueltos?

Porque recibo estos errores cuando intento compilar:

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

Ya puedo decir que el problema no es de un enlace incorrecto. He vinculado todo correctamente, pero por alguna razón no se compilará.

Estoy tratando de usar SDL2.

Estoy usando Visual Studio 2015 como compilador.

Me he vinculado a SDL2.lib y SDL2main.lib en Linker -> Input -> Dependencias adicionales y me he asegurado de que los directorios de VC ++ sean correctos.


Como se respondió anteriormente, la respuesta correcta es compilar todo con VS2015, pero por interés, el siguiente es mi análisis del problema.

Este símbolo no parece estar definido en ninguna biblioteca estática proporcionada por Microsoft como parte de VS2015, lo cual es bastante peculiar ya que todos los demás lo están. Para descubrir por qué, debemos mirar la declaración de esa función y, lo que es más importante, cómo se usa.

Aquí hay un fragmento de los encabezados de Visual Studio 2008:

_CRTIMP FILE * __cdecl __iob_func(void); #define stdin (&__iob_func()[0]) #define stdout (&__iob_func()[1]) #define stderr (&__iob_func()[2])

Por lo tanto, podemos ver que el trabajo de la función es devolver el inicio de una matriz de objetos FILE (no identificadores, el "FILE *" es el identificador, FILE es la estructura de datos opaca subyacente que almacena los elementos importantes del estado). Los usuarios de esta función son las tres macros stdin, stdout y stderr que se utilizan para varias llamadas de estilo fscanf, fprintf.

Ahora echemos un vistazo a cómo Visual Studio 2015 define las mismas cosas:

_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned); #define stdin (__acrt_iob_func(0)) #define stdout (__acrt_iob_func(1)) #define stderr (__acrt_iob_func(2))

Por lo tanto, el enfoque ha cambiado para que la función de reemplazo ahora devuelva el identificador de archivo en lugar de la dirección de la matriz de objetos de archivo, y las macros han cambiado para simplemente llamar a la función pasando un número de identificación.

Entonces, ¿por qué no pueden ellos / nosotros proporcionar una API compatible? Hay dos reglas clave que Microsoft no puede contravenir en términos de su implementación original a través de __iob_func:

  1. Debe haber una matriz de tres estructuras FILE que se puedan indexar de la misma manera que antes.
  2. El diseño estructural de FILE no puede cambiar.

Cualquier cambio en cualquiera de los anteriores significaría que el código compilado existente vinculado contra eso iría muy mal si se llama a esa API.

Echemos un vistazo a cómo se definió / FILE.

Primero la definición de ARCHIVO VS2008:

struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; }; typedef struct _iobuf FILE;

Y ahora la definición de ARCHIVO VS2015:

typedef struct _iobuf { void* _Placeholder; } FILE;

Entonces está el quid de la cuestión: la estructura ha cambiado de forma. El código compilado existente que se refiere a __iob_func se basa en el hecho de que los datos devueltos son tanto una matriz que puede indexarse ​​como que en esa matriz los elementos están separados por la misma distancia.

Las posibles soluciones mencionadas en las respuestas anteriores a lo largo de estas líneas no funcionarían (si se llama) por algunas razones:

FILE _iob[] = {*stdin, *stdout, *stderr}; extern "C" FILE * __cdecl __iob_func(void) { return _iob; }

La matriz FILE _iob se compilaría con VS2015 y, por lo tanto, se presentaría como un bloque de estructuras que contienen un vacío *. Suponiendo una alineación de 32 bits, estos elementos estarían separados por 4 bytes. Entonces _iob [0] está en el desplazamiento 0, _iob [1] está en el desplazamiento 4 y _iob [2] está en el desplazamiento 8. En cambio, el código de llamada esperará que el ARCHIVO sea mucho más largo, alineado a 32 bytes en mi sistema, y ​​así tomará la dirección de la matriz devuelta y agregará 0 bytes para llegar al elemento cero (ese está bien), pero para _iob [1] deducirá que necesita agregar 32 bytes y para _iob [2] deducirá que necesita agregar 64 bytes (porque así es como se ve en los encabezados VS2008). Y, de hecho, el código desmontado para VS2008 lo demuestra.

Un problema secundario con la solución anterior es que copia el contenido de la estructura FILE (* stdin), no el identificador FILE *. Por lo tanto, cualquier código VS2008 buscaría una estructura subyacente diferente a VS2015. Esto podría funcionar si la estructura solo contuviera punteros, pero eso es un gran riesgo. En cualquier caso, el primer problema hace que esto sea irrelevante.

El único truco que he podido imaginar es uno en el que __iob_func recorre la pila de llamadas para determinar qué identificador de archivo real están buscando (en función del desplazamiento agregado a la dirección devuelta) y devuelve un valor calculado de tal manera que da la respuesta correcta Esto es tan loco como parece, pero el prototipo solo para x86 (no x64) se enumera a continuación para su diversión. Funcionó bien en mis experimentos, pero su kilometraje puede variar, ¡no recomendado para uso en producción!

#include <windows.h> #include <stdio.h> #include <dbghelp.h> /* #define LOG */ #if defined(_M_IX86) #define GET_CURRENT_CONTEXT(c, contextFlags) / do { / c.ContextFlags = contextFlags; / __asm call x / __asm x: pop eax / __asm mov c.Eip, eax / __asm mov c.Ebp, ebp / __asm mov c.Esp, esp / } while(0); #else /* This should work for 64-bit apps, but doesn''t */ #define GET_CURRENT_CONTEXT(c, contextFlags) / do { / c.ContextFlags = contextFlags; / RtlCaptureContext(&c); / } while(0); #endif FILE * __cdecl __iob_func(void) { CONTEXT c = { 0 }; STACKFRAME64 s = { 0 }; DWORD imageType; HANDLE hThread = GetCurrentThread(); HANDLE hProcess = GetCurrentProcess(); GET_CURRENT_CONTEXT(c, CONTEXT_FULL); #ifdef _M_IX86 imageType = IMAGE_FILE_MACHINE_I386; s.AddrPC.Offset = c.Eip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Ebp; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Offset = c.Esp; s.AddrStack.Mode = AddrModeFlat; #elif _M_X64 imageType = IMAGE_FILE_MACHINE_AMD64; s.AddrPC.Offset = c.Rip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Rsp; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Offset = c.Rsp; s.AddrStack.Mode = AddrModeFlat; #elif _M_IA64 imageType = IMAGE_FILE_MACHINE_IA64; s.AddrPC.Offset = c.StIIP; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.IntSp; s.AddrFrame.Mode = AddrModeFlat; s.AddrBStore.Offset = c.RsBSP; s.AddrBStore.Mode = AddrModeFlat; s.AddrStack.Offset = c.IntSp; s.AddrStack.Mode = AddrModeFlat; #else #error "Platform not supported!" #endif if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { #ifdef LOG printf("Error: 0x%08X (Address: %p)/n", GetLastError(), (LPVOID)s.AddrPC.Offset); #endif return NULL; } if (s.AddrReturn.Offset == 0) { return NULL; } { unsigned char const * assembly = (unsigned char const *)(s.AddrReturn.Offset); #ifdef LOG printf("Code bytes proceeding call to __iob_func: %p: %02X,%02X,%02X/n", assembly, *assembly, *(assembly + 1), *(assembly + 2)); #endif if (*assembly == 0x83 && *(assembly + 1) == 0xC0 && (*(assembly + 2) == 0x20 || *(assembly + 2) == 0x40)) { if (*(assembly + 2) == 32) { return (FILE*)((unsigned char *)stdout - 32); } if (*(assembly + 2) == 64) { return (FILE*)((unsigned char *)stderr - 64); } } else { return stdin; } } return NULL; }


En mi caso, este error proviene de mi versión de prueba para eliminar dependencias de la biblioteca de tiempo de ejecución dependiente de la versión MSVC DLL (msvcr10.dll más o menos) y / o eliminar la biblioteca de tiempo de ejecución estática también, para eliminar el exceso de grasa de mis ejecutables.

Así que uso el modificador de enlace / NODEFAULTLIB, mis entradas "msvcrt-light.lib" (google para cuando sea necesario) y mainCRTStartup() / WinMainCRTStartup() .

Es en mi humilde opinión desde Visual Studio 2015, así que me quedé con los compiladores más antiguos.

Sin embargo, definir el símbolo _NO_CRT_STDIO_INLINE elimina todas las molestias, y una simple aplicación "Hello World" es de nuevo 3 KB pequeña y no depende de archivos DLL inusuales. Probado en Visual Studio 2017.


Esto puede suceder cuando se vincula a msvcrt.dll en lugar de msvcr10.dll (o similar), que es un buen plan. Porque lo liberará para redistribuir la biblioteca de tiempo de ejecución de Visual Studio dentro de su paquete de software final.

Esa solución alternativa me ayuda (en Visual Studio 2008):

main.obj : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _GenerateInfoFile

Este fragmento no es necesario para Visual Studio 6 y su compilador. Por lo tanto, el #ifdef.


Me las arreglé para solucionar el problema.

La fuente del error fue esta línea de código, que se puede encontrar en el código fuente principal de SDL.

fprintf("%s: %s/n", title, message);

Entonces, lo que hice fue editar el código fuente en SDLmain de esa línea también:

#if _MSC_VER >= 1400 #undef stdin #undef stdout #undef stderr extern "C" _CRTIMP extern FILE _iob[]; #define stdin _iob #define stdout (_iob+1) #define stderr (_iob+2) #endif

Y luego construí el SDLmain y copié y reemplacé el viejo SDLmain.lib en mi directorio de la biblioteca SDL2 con el recién construido y editado.

Luego, cuando ejecuté mi programa con SDL2, no aparecieron mensajes de error y el código funcionó sin problemas.

No sé si esto me morderá más tarde, pero así todo va bien.


Mi consejo es que no (intente) implementar __iob_func.

Mientras corrige estos errores:

libpngd.v110.lib(pngrutil.obj) : error LNK2001: unresolved external symbol ___iob_func curllib.v110.lib(mprintf.obj) : error LNK2001: unresolved external symbol ___iob_func

Probé las soluciones de las otras respuestas, pero al final, devolver un FILE* C-array no coincide con una matriz de estructuras IOB internas de Windows. @Volker tiene razón en que nunca funcionará para más de uno de stdin , stdout o stderr .

Si una biblioteca realmente UTILIZA una de esas transmisiones, se bloqueará . Mientras su programa no haga que la lib los use, nunca lo sabrá . Por ejemplo, png_default_error escribe en stderr cuando el CRC no coincide con los metadatos de PNG. (Normalmente no es un problema digno de un choque)

Conclusión: no es posible mezclar VS2012 (Platform Toolset v110 / v110_xp) y las bibliotecas VS2015 +, si usan stdin, stdout y / o stderr.

Solución: recompile sus bibliotecas que tienen símbolos __iob_func sin resolver con su versión actual de VS y un conjunto de herramientas de plataforma correspondiente.


Microsoft tiene una nota especial sobre esto ( https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT ):

La familia de funciones printf y scanf ahora se definen en línea.

Las definiciones de todas las funciones printf y scanf se han movido en línea a stdio.h , conio.h y otros encabezados CRT. Este es un cambio importante que conduce a un error de enlazador (LNK2019, símbolo externo sin resolver) para cualquier programa que declare estas funciones localmente sin incluir los encabezados CRT apropiados. Si es posible, debe actualizar el código para incluir los encabezados CRT (es decir, agregar #include) y las funciones en línea, pero si no desea modificar su código para incluir estos archivos de encabezado, una solución alternativa es agregar un adicional biblioteca a su entrada de enlazador, legacy_stdio_definitions.lib .

Para agregar esta biblioteca a su entrada de vinculador en el IDE, abra el menú contextual para el nodo del proyecto, elija Propiedades, luego en el cuadro de diálogo Propiedades del proyecto, elija Vinculador y edite la Entrada de vinculador para agregar legacy_stdio_definitions.lib al punto y coma lista separada.

Si su proyecto se vincula con bibliotecas estáticas que se compilaron con una versión de Visual C ++ anterior a 2015, el vinculador podría informar un símbolo externo no resuelto. Estos errores pueden hacer referencia a definiciones stdio internas para _iob , _iob_func o importaciones relacionadas para ciertas funciones stdio en forma de __imp_ * . Microsoft recomienda que recompile todas las bibliotecas estáticas con la última versión del compilador y las bibliotecas de Visual C ++ cuando actualice un proyecto. Si la biblioteca es una biblioteca de terceros para la cual la fuente no está disponible, debe solicitar un binario actualizado de la tercera parte o encapsular su uso de esa biblioteca en una DLL separada que compila con la versión anterior del compilador de Visual C ++ y bibliotecas.


No sé por qué pero:

#ifdef main #undef main #endif

Después de la incluye, pero antes de que su principal debe solucionarlo desde mi experiencia.


Para Milan Babuškov, OMI, esto es exactamente lo que debería ser la función de reemplazo :-)

FILE _iob[] = {*stdin, *stdout, *stderr}; extern "C" FILE * __cdecl __iob_func(void) { return _iob; }


Para cualquiera que todavía esté buscando una respuesta donde los trucos anteriores no funcionaron. La vinculación estática es la forma de resolver este problema. Cambie la configuración de su biblioteca de tiempo de ejecución de la siguiente manera

Project properties --> C/C++ --> Code generation --> Runtime Library --> Multi-threaded Debug (/MTd) instead of /MDd


Para generar más confusión en este hilo ya rico, me topé con el mismo externo no resuelto en fprintf

FILE* __cdecl __iob_func(void) { FILE _iob[] = { *stdin, *stdout, *stderr }; return _iob; }

Incluso si en mi caso fue en un contexto bastante diferente: en Visual Studio 2005 (Visual Studio 8.0) y el error estaba ocurriendo en mi propio código (el mismo que estaba compilando), no un tercero.

Sucedió que este error fue activado por la opción / MD en mis banderas de compilación. Cambiar a / MT eliminó el problema. Esto es extraño porque generalmente, vincular estáticamente (MT) plantea más problemas que dinámicamente (MD) ... pero en caso de que sirva a otros, lo pongo allí.


Resuelvo este problema con la siguiente función. Yo uso Visual Studio 2019.

fprintf(stderr, "%s: %s/n", title, message);

debido a que la llamada a la función definida por la macro stdin, la expresión "* stdin" no se puede usar como inicializador de matriz global. Pero la matriz local más inicial es posible. lo siento, soy pobre en inglés.


Tuve el mismo problema en VS2015. Lo resolví compilando las fuentes SDL2 en VS2015.

  1. Vaya a http://libsdl.org/download-2.0.php y descargue el código fuente SDL 2.
  2. Abra SDL_VS2013.sln en VS2015 . Se le pedirá que convierta los proyectos. Hazlo.
  3. Compilar proyecto SDL2.
  4. Compile el proyecto SDL2main.
  5. Use los nuevos archivos de salida generados SDL2main.lib, SDL2.lib y SDL2.dll en su proyecto SDL 2 en VS2015.



Vincular significa no funcionar correctamente. Excavando en stdio.h de VS2012 y VS2015, lo siguiente funcionó para mí. Por desgracia, debe decidir si debería funcionar para uno de {stdin, stdout, stderr}, nunca más de uno.

extern "C" FILE* __cdecl __iob_func() { struct _iobuf_VS2012 { // .../Microsoft Visual Studio 11.0/VC/include/stdio.h #56 char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; }; // VS2015 has only FILE = struct {void*} int const count = sizeof(_iobuf_VS2012) / sizeof(FILE); //// stdout //return (FILE*)(&(__acrt_iob_func(1)->_Placeholder) - count); // stderr return (FILE*)(&(__acrt_iob_func(2)->_Placeholder) - 2 * count); }