Quelqu'un pourrait-il expliquer ce que le
__imp__fprintf
et
__imp____iob_func
moyens externes non résolus?
Parce que je reçois ces erreurs lorsque j'essaie de compiler:
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
Je peux déjà dire que le problème ne vient pas d'un mauvais lien. J'ai tout lié correctement, mais pour une raison quelconque, il ne sera pas compilé.
J'essaie d'utiliser SDL2.
J'utilise Visual Studio 2015 en tant que compilateur.
J'ai lié à SDL2.lib et SDL2main.lib dans l'éditeur de liens -> Entrée -> Dépendances supplémentaires et je me suis assuré que les répertoires VC++ étaient corrects.
J'ai enfin compris pourquoi cela se produit!
Dans Visual Studio 2015, stdin, stderr, stdout sont définis comme suit:
#define stdin (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))
Mais auparavant, ils étaient définis comme suit:
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])
Donc, maintenant, __iob_func n'est plus défini, ce qui entraîne une erreur de lien lorsque vous utilisez un fichier .lib compilé avec les versions précédentes de visual studio.
Pour résoudre le problème, vous pouvez essayer de définir vous-même __iob_func()
, ce qui devrait renvoyer un tableau contenant {*stdin,*stdout,*stderr}
.
En ce qui concerne les autres erreurs de lien concernant les fonctions stdio (dans mon cas, c’était sprintf()
), vous pouvez ajouter legacy_stdio_definitions.lib à vos options d’éditeur de liens.
Pour Milan Babuškov, de l’OMI, c’est exactement à quoi devrait ressembler la fonction de remplacement :-)
FILE _iob[] = {*stdin, *stdout, *stderr};
extern "C" FILE * __cdecl __iob_func(void)
{
return _iob;
}
Microsoft a une note spéciale à ce sujet ( https://msdn.Microsoft.com/en-us/library/bb531344.aspx#BK_CRT ):
Les familles de fonctions printf et scanf sont maintenant définies en ligne.
Les définitions de toutes les fonctions printf et scanf ont été déplacées en ligne dans stdio.h , conio.h , et d'autres en-têtes CRT. Il s’agit d’un changement décisif qui entraîne une erreur de l'éditeur de liens (LNK2019, symbole externe non résolu) pour tous les programmes ayant déclaré ces fonctions localement sans inclure les en-têtes CRT appropriés. Si possible, vous devez mettre à jour le code pour inclure les en-têtes CRT (c'est-à-dire, ajouter #include) et les fonctions en ligne, mais si vous ne souhaitez pas modifier votre code pour inclure ces fichiers d'en-tête, une autre solution consiste à ajouter un élément supplémentaire. bibliothèque à votre entrée de l'éditeur de liens, legacy_stdio_definitions.lib .
Pour ajouter cette bibliothèque à votre entrée de l'éditeur de liens dans l'EDI, ouvrez le menu contextuel du nœud du projet, choisissez Propriétés, puis dans la boîte de dialogue Propriétés du projet, choisissez l'éditeur de liens et modifiez l'entrée de l'éditeur de liens pour ajouter legacy_stdio_definitions.lib au point-virgule. liste séparée.
Si votre projet est lié à des bibliothèques statiques compilées avec une version de Visual C++ antérieure à 2015, l'éditeur de liens peut signaler un symbole externe non résolu. Ces erreurs peuvent faire référence à des définitions stdio internes pour _ iob , _ iob_func ou connexes. importations pour certaines fonctions stdio sous la forme de __ imp _ * . Microsoft vous recommande de recompiler toutes les bibliothèques statiques avec la dernière version du compilateur Visual C++ et les bibliothèques lorsque vous mettez à niveau un projet. Si la bibliothèque est une bibliothèque tierce pour laquelle la source n'est pas disponible, vous devez demander un binaire mis à jour au tiers ou encapsuler votre utilisation de cette bibliothèque dans un DLL séparé que vous compilez avec l'ancien. version du compilateur Visual C++ et des bibliothèques.
J'ai eu le même problème dans VS2015. Je l'ai résolu en compilant les sources SDL2 dans VS2015.
Comme indiqué ci-dessus, la bonne solution consiste à tout compiler avec VS2015, mais voici mon analyse du problème qui intéresse.
Ce symbole ne semble pas être défini dans les bibliothèques statiques fournies par Microsoft dans le cadre de VS2015, ce qui est assez particulier puisque tous les autres le sont. Pour découvrir pourquoi, nous devons examiner la déclaration de cette fonction et, plus important encore, son utilisation.
Voici un extrait des en-têtes 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])
Ainsi, nous pouvons voir que le travail de la fonction est de renvoyer le début d'un tableau d'objets FILE (pas de descripteurs, le "FILE *" est le descripteur, FILE est la structure de données opaque sous-jacente stockant les goodies d'état importants). Les utilisateurs de cette fonction sont les trois macros stdin, stdout et stderr, utilisées pour divers appels de style fscanf, fprintf.
Voyons maintenant comment Visual Studio 2015 définit les mêmes choses:
_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))
L'approche a donc changé pour que la fonction de remplacement renvoie maintenant le descripteur de fichier plutôt que l'adresse du tableau d'objets de fichier, et les macros ont été modifiées pour appeler simplement la fonction transmettant un numéro d'identification.
Alors, pourquoi ne peuvent-ils pas/nous fournissons une API compatible? Il y a deux règles clés que Microsoft ne peut pas enfreindre en termes de leur implémentation d'origine via __iob_func:
Toute modification apportée à l’un des éléments ci-dessus signifierait que le code compilé existant lié à ce dernier irait très mal si cette API est appelée.
Voyons comment FILE était/est défini.
Tout d’abord la définition du fichier VS2008:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
Et maintenant, la définition du fichier VS2015:
typedef struct _iobuf
{
void* _Placeholder;
} FILE;
Donc, il y a l'essentiel: la structure a changé de forme. Le code compilé existant faisant référence à __iob_func repose sur le fait que les données renvoyées sont à la fois un tableau pouvant être indexé et que, dans ce tableau, les éléments sont à la même distance.
Les solutions possibles mentionnées dans les réponses ci-dessus dans ce sens ne fonctionneraient pas (si elles étaient appelées) pour plusieurs raisons:
FILE _iob[] = {*stdin, *stdout, *stderr};
extern "C" FILE * __cdecl __iob_func(void)
{
return _iob;
}
Le tableau FILE _iob serait compilé avec VS2015 et serait donc présenté sous forme de bloc de structures contenant un vide *. En supposant un alignement sur 32 bits, ces éléments seraient séparés de 4 octets. Donc, _iob [0] est à l'offset 0, _iob [1] à l'offset 4 et _iob [2] à l'offset 8. Le code appelant s'attendra à ce que FILE soit beaucoup plus long, aligné à 32 octets sur mon système, etc. il faudra l'adresse du tableau retourné et ajouter 0 octet pour atteindre l'élément zéro (celui-ci est correct), mais pour _iob [1], il en déduira qu'il doit ajouter 32 octets et pour _iob [2], il en déduira qu’il doit ajouter 64 octets (car c’est ce qui se présentait dans les en-têtes de VS2008). Et en effet, le code désassemblé de VS2008 le montre bien.
Un problème secondaire avec la solution ci-dessus est qu'il copie le contenu de la structure FILE (* stdin), pas le descripteur FILE *. Ainsi, tout code VS2008 consisterait en une structure sous-jacente différente de VS2015. Cela pourrait fonctionner si la structure ne contenait que des pointeurs, mais c'est un gros risque. En tout état de cause, le premier problème rend cette question non pertinente.
Le seul piratage que j'ai pu imaginer est celui dans lequel __iob_func guide la pile d'appels pour déterminer le descripteur de fichier recherché (sur la base du décalage ajouté à l'adresse renvoyée) et renvoie une valeur calculée telle qu'il donne la bonne réponse. C'est tout aussi fou que cela puisse paraître, mais le prototype pour x86 seulement (pas x64) est listé ci-dessous pour votre divertissement. Cela a bien fonctionné dans mes expériences, mais votre kilométrage peut varier - déconseillé pour une utilisation en production!
#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;
}
Je ne sais pas pourquoi mais:
#ifdef main
#undef main
#endif
Après l'inclusion mais avant votre principale devrait le réparer de mon expérience.
Une solution plus récente à ce problème: utilisez les bibliothèques sdl plus récentes sur
" https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/?C=M;O=D "
Ils semblent avoir résolu le problème, même s'il ne s'agit que de la bibliothèque 32 bits (je pense).
Lier signifie ne pas fonctionner correctement. Creuser dans stdio.h de VS2012 et VS2015 a fonctionné comme suit: Hélas, vous devez décider si cela devrait fonctionner pour l'un des {stdin, stdout, stderr}, jamais plus d'un.
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);
}
Mon conseil est de ne pas (essayer de) implémenter __iob_func.
En corrigeant ces erreurs:
libpngd.v110.lib(pngrutil.obj) : error LNK2001: unresolved external symbol ___iob_func curllib.v110.lib(mprintf.obj) : error LNK2001: unresolved external symbol ___iob_func
J'ai essayé les solutions des autres réponses, mais finalement, renvoyer un C-array FILE*
ne correspond pas à un tableau de structures IOB internes de Windows. @Volker a raison de dire que cela ne fonctionnera jamais avec plus d'un de stdin
, stdout
ou stderr
.
Si une bibliothèque utilise réellement l'un de ces flux, il plantera. Tant que votre programme ne fait pas en sorte que la bibliothèque les utilise, vous ne saurez jamais. Par exemple, png_default_error
écrit dans stderr
lorsque le CRC ne correspond pas dans les métadonnées du PNG. (Normalement, pas un problème digne d'un crash)
Conclusion: Il n'est pas possible de mélanger les bibliothèques VS2012 (Platform Toolset v110/v110_xp) et VS2015 + si elles utilisent stdin, stdout et/ou stderr.
Solution: recompilez vos bibliothèques comportant __iob_func
symboles non résolus avec votre version actuelle de VS et un ensemble d'outils de plateforme correspondant.
Utilisez les fichiers SDL2main.lib et SDL.lib précompilés pour la bibliothèque de votre projet VS2015: https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225.Zip
Je résous ce problème avec la fonction suivante. J'utilise Visual Studio 2019.
FILE* __cdecl __iob_func(void)
{
FILE _iob[] = { *stdin, *stdout, *stderr };
return _iob;
}
parce que l'appel de la fonction définie par la macro stdin, l'expression "* stdin" est impossible avec l'initialiseur de tableau global. Mais l'initialisation locale du tableau est possible. désolé, je suis pauvre en anglais.
J'ai réussi à résoudre le problème.
La source de l'erreur était cette ligne de code, que l'on peut trouver dans le code source SDLmain.
fprintf(stderr, "%s: %s\n", title, message);
Donc, ce que j’ai fait, c’est aussi de modifier le code source dans SDLmain de cette ligne:
fprintf("%s: %s\n", title, message);
Et puis, j'ai construit SDLmain, puis copié et remplacé l'ancien SDLmain.lib dans le répertoire de ma bibliothèque SDL2 par le nouveau construit et modifié.
Ensuite, lorsque j'ai exécuté mon programme avec SDL2, aucun message d'erreur n'est apparu et le code s'est déroulé correctement.
Je ne sais pas si cela va me mordre plus tard, mais pour tout va bien.
Pour tous ceux qui cherchent encore une réponse où les astuces ci-dessus ne fonctionnent pas. La liaison statique est le moyen de résoudre ce problème. Modifiez les paramètres de votre bibliothèque d'exécution comme indiqué ci-dessous.
Project properties --> C/C++ --> Code generation --> Runtime Library --> Multi-threaded Debug (/MTd) instead of /MDd
Cela peut arriver lorsque vous créez un lien vers msvcrt.dll au lieu de msvcr10.dll (ou similaire), ce qui est un bon plan. Cela vous permettra de redistribuer la bibliothèque d'exécution de Visual Studio dans votre package logiciel final.
Cette solution de contournement m'aide (dans Visual Studio 2008):
#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
Cet extrait n'est pas nécessaire pour Visual Studio 6 et son compilateur. Par conséquent le #ifdef.
Dans mon cas, cette erreur provient de mon essai visant à supprimer les dépendances de la bibliothèque d'exécution dépendante de la version MSVC DLL (msvcr10.dll ou autre) et/ou à supprimer également la bibliothèque d'exécution statique, afin de supprimer l'excès de graisse de mes exécutables. .
J'utilise donc le commutateur/NODEFAULTLIB de l'éditeur de liens, mes propres entrées "msvcrt-light.lib" (google lorsque vous en avez besoin) et mainCRTStartup()
/WinMainCRTStartup()
.
C'est à mon humble avis depuis Visual Studio 2015, donc je suis resté sur les anciens compilateurs.
Cependant, définir le symbole _ NO_CRT_STDIO_INLINE supprime tous les tracas et une simple application "Hello World" a à nouveau une taille de 3 Ko et ne dépend pas de DLL inhabituelles. Testé dans Visual Studio 2017.
Pour apporter plus de confusion dans ce fil déjà riche, il m'est arrivé de tomber sur le même externe non résolu sur fprintf
main.obj : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _GenerateInfoFile
Même si dans mon cas, le contexte était assez différent: sous Visual Studio 2005 (Visual Studio 8.0) et que l'erreur se produisait dans mon propre code (le même que je compilais), pas un tiers.
Il est arrivé que cette erreur soit déclenchée par l'option/MD dans les drapeaux du compilateur. Le passage à/MT a résolu le problème. C'est bizarre parce qu'habituellement, lier statiquement (MT) pose plus de problème que dynamiquement (MD) ... mais juste au cas où il en servirait un autre, je l'ai mis là.