未解析的外部符号 __imp__fprintf 和 __imp____iob_func,SDL2

unresolved external symbol __imp__fprintf and __imp____iob_func, SDL2

有人可以解释一下

__imp__fprintf

__imp____iob_func

未解决的外部手段?

因为我在尝试编译时遇到了这些错误:

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

我已经可以说问题不是链接错误了。我已正确链接所有内容,但由于某种原因无法编译。

我正在尝试使用 SDL2。

我使用 Visual Studio 2015 作为编译器。

我已经在链接器 -> 输入 -> 附加依赖项中链接到 SDL2.lib 和 SDL2main.lib,并且我确保 VC++ 目录是正确的。

我设法解决了这个问题。

错误的来源是这行代码,可以在SDLmain源代码中找到。

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

所以我所做的也是在该行的 SDLmain 中编辑源代码:

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

然后我构建了 SDLmain 并复制并用新构建和编辑的 SDL2 库目录替换了旧的 SDLmain.lib。

然后当我 运行 我的 SDL2 程序没有出现错误消息并且编码 运行 顺利。

我不知道这是否会影响我,但一切都很顺利。

我终于明白为什么会这样了!

在visual studio2015年,stdin、stderr、stdout定义如下:

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

但之前,它们被定义为:

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

所以现在 __iob_func 不再定义,这会导致 link 使用以前版本的 visual studio.

编译的 .lib 文件时出现错误

要解决这个问题,您可以尝试自己定义 __iob_func() 应该 return 一个包含 {*stdin,*stdout,*stderr}.

的数组

关于 stdio 函数的其他 link 错误(在我的例子中是 sprintf()),您可以将 legacy_stdio_definitions.lib 添加到您的link其他选项。

我在 VS2015 中遇到了同样的问题。我已经通过在 VS2015 中编译 SDL2 源解决了它。

  1. 转到 http://libsdl.org/download-2.0.php 并下载 SDL 2 源代码。
  2. VS2015 中打开 SDL_VS2013.sln。您将被要求转换项目。做吧。
  3. 编译SDL2项目。
  4. 编译SDL2main项目。
  5. 在 VS2015 的 SDL 2 项目中使用新生成的输出文件 SDL2main.lib、SDL2.lib 和 SDL2.dll。

此问题的最新解决方案:在

上使用更新的 sdl 库

"https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/?C=M;O=D"

他们似乎已经解决了这个问题,尽管它只是 32 位库(我认为)。

对于 Milan Babuškov,IMO,这正是替换函数的样子:-)

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

当您 link 到 msvcrt.dll 而不是 msvcr10.dll(或类似的)时,这可能会发生,这是一个很好的计划。因为它可以让您自由地在最终软件包中重新分发 Visual Studio 的运行时库。

该解决方法对我有帮助(在 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

Visual Studio6 及其编译器不需要此代码段。因此 #ifdef.

为您的 VS2015 项目库使用预编译的 SDL2main.lib 和 SDL.lib: https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225.zip

如上回答,正确答案是用VS2015全部编译,不过为了兴趣,下面是我对问题的分析。

这个符号似乎没有在 Microsoft 作为 VS2015 的一部分提供的任何静态库中定义,这很奇怪,因为所有其他符号都是。要找出原因,我们需要查看该函数的声明,更重要的是,它是如何使用的。

这是 Visual Studio 2008 headers 的片段:

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

所以我们可以看到函数的工作是return一个FILE数组的开始objects(不是句柄,"FILE *"是句柄,FILE是存储重要状态信息的底层不透明数据结构)。该函数的使用者是stdin、stdout和stderr这三个宏,用于各种fscanf、fprintf风格的调用。

现在让我们来看看Visual Studio 2015 是如何定义相同的东西的:

_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))

因此替换函数的方法现在已更改为 return 文件句柄而不是文件数组的地址 objects,并且宏已更改为简单地调用函数传递在识别号码中。

那么为什么 they/we 不能提供兼容的 API? 有两个关键规则,Microsoft 不能通过 __iob_func:

违反其原始实现
  1. 必须有一个包含三个 FILE 结构的数组,可以用与以前相同的方式进行索引。
  2. FILE 的结构布局不能改变。

以上任何一项的任何更改都意味着如果调用 API 链接到的现有编译代码将出现严重错误。

我们来看看FILEwas/is是怎么定义的

首先是VS2008 FILE定义:

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

现在 VS2015 文件定义:

typedef struct _iobuf
{
    void* _Placeholder;
} FILE;

所以问题的症结在于:结构已经改变了形状。引用 __iob_func 的现有编译代码依赖于这样一个事实,即数据 returned 既是一个可以被索引的数组,并且在该数组中元素之间的距离相同。

由于以下几个原因,上述答案中提到的可能解决方案(如果调用)将不起作用:

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

FILE 数组 _iob 将使用 VS2015 进行编译,因此它将被布置为包含 void* 的结构块。假设 32 位对齐,这些元素将相隔 4 个字节。所以 _iob[0] 位于偏移量 0,_iob[1] 位于偏移量 4,_iob[2] 位于偏移量 8。调用代码将期望 FILE 更长,在我的系统上以 32 字节对齐,等等它将获取 returned 数组的地址并添加 0 个字节以到达元素零(那个没问题),但是对于 _iob[1] 它将推断它需要添加 32 个字节并且对于 _iob[ 2] 它将推断它需要添加 64 个字节(因为它在 VS2008 headers 中看起来就是这样)。事实上,VS2008 的反汇编代码证明了这一点。

上述解决方案的第二个问题是它复制 FILE 结构 (*stdin) 的内容,而不是 FILE * 句柄。因此,任何 VS2008 代码都将查看与 VS2015 不同的底层结构。如果结构只包含指针,这可能会起作用,但这是一个很大的风险。无论如何,第一个问题使这个无关紧要。

我唯一能想到的技巧是 __iob_func 遍历调用堆栈以找出他们正在寻找的实际文件句柄(基于添加到 returned address) 和 returns 一个计算值,以便它给出正确的答案。这听起来有点疯狂,但下面列出了仅用于 x86(不是 x64)的原型供您娱乐。它在我的实验中运行良好,但你的里程可能会有所不同 - 不推荐用于生产!

#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;
}

到link表示工作不正常。深入研究 VS2012 和 VS2015 的 stdio.h 以下内容对我有用。 las,您必须决定它是否适用于 { stdin, stdout, stderr } 之一,绝不能超过一个。

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);
}

微软对此有特别说明(https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT):

The printf and scanf family of functions are now defined inline.

The definitions of all of the printf and scanf functions have been moved inline into stdio.h, conio.h, and other CRT headers. This is a breaking change that leads to a linker error (LNK2019, unresolved external symbol) for any programs that declared these functions locally without including the appropriate CRT headers. If possible, you should update the code to include the CRT headers (that is, add #include ) and the inline functions, but if you do not want to modify your code to include these header files, an alternative solution is to add an additional library to your linker input, legacy_stdio_definitions.lib.

To add this library to your linker input in the IDE, open the context menu for the project node, choose Properties, then in the Project Properties dialog box, choose Linker, and edit the Linker Input to add legacy_stdio_definitions.lib to the semi-colon-separated list.

If your project links with static libraries that were compiled with a release of Visual C++ earlier than 2015, the linker might report an unresolved external symbol. These errors might reference internal stdio definitions for _iob, _iob_func, or related imports for certain stdio functions in the form of __imp_*. Microsoft recommends that you recompile all static libraries with the latest version of the Visual C++ compiler and libraries when you upgrade a project. If the library is a third-party library for which source is not available, you should either request an updated binary from the third party or encapsulate your usage of that library into a separate DLL that you compile with the older version of the Visual C++ compiler and libraries.

我不知道为什么但是:

#ifdef main
#undef main
#endif

在 includes 之后但在你的 main 之前应该根据我的经验修复它。

我的建议是不要(尝试)实施 __iob_func。

修复这些错误时:

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

我尝试了其他答案的解决方案,但最终返回的 FILE* C 数组与 Windows' 内部 IOB 结构的数组不匹配。 @Volker 是对的,它永远不会对 stdinstdoutstderr.

中的一个以上起作用

如果库实际使用其中一个流,它将崩溃。只要您的程序不导致库使用它们,您永远不会知道。例如,当 PNG 的元数据中的 CRC 不匹配时,png_default_error 写入 stderr。 (通常不是一个值得崩溃的问题)

结论:不可能混合使用 VS2012(平台工具集 v110/v110_xp)和 VS2015+ 库,如果它们使用标准输入、标准输出 and/or 标准错误。

解决方案:使用当前版本的 VS 和匹配的平台工具集重新编译具有 __iob_func 未解析符号的库。

为了给这个已经很丰富的线程带来更多混乱,我碰巧在 fprintf 上遇到了相同的未解决外部问题

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

即使在我的情况下它处于一个相当不同的环境中:在 Visual Studio 2005 (Visual Studio 8.0) 下并且错误发生在我自己的代码中(与我正在编译的代码完全相同) ,不是第三方。

碰巧这个错误是由我的编译器标志中的 /MD 选项触发的。切换到 /MT 解决了这个问题。这很奇怪,因为通常情况下,静态链接 (MT) 比动态链接 (MD) 会引发更多问题……但以防万一它服务于其他人,我把它放在那里。

在我的例子中,这个错误来自于我尝试删除对 MSVC 版本依赖的运行时库 DLL 的依赖(msvcr10.dll 左右)and/or 也删除了静态运行时库,以删除多余的脂肪来自我的可执行文件。

所以我使用 /NODEFAULTLIB 链接器开关,我自制的 "msvcrt-light.lib"(google 在你需要的时候),以及 mainCRTStartup() / WinMainCRTStartup() 条目。

恕我直言,自 Visual Studio 2015 年以来,所以我坚持使用较旧的编译器。

然而,定义符号 _NO_CRT_STDIO_INLINE 消除了所有麻烦,并且一个简单的 "Hello World" 应用程序又是 3 KB 小并且不依赖于不寻常的 DLL . 2017 年 Visual Studio 测试。

我用下面的函数解决了这个问题。我用 Visual Studio 2019.

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

因为 stdin 宏定义函数调用,"*stdin" 表达式不能使用全局数组初始值设定项。但是本地数组初始值设定项是可能的。 不好意思,我英语不好

对于仍在寻找上述技巧不起作用的答案的任何人。静态链接是解决这个问题的方法。如下更改您的运行时库设置

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

这里有关于这个解决方案的讨论:https://social.msdn.microsoft.com/Forums/vstudio/en-US/4a1c9610-fa41-45f6-ad39-c9f6795be6f2/msvcrt-iob-disappeared?forum=vclanguage

将此代码粘贴到您的任何源文件中并重新构建。 对我有用!

 #include <stdio.h>

FILE _iob[3];

FILE* __cdecl __iob_func(void)
{

   _iob[0] = *stdin;

   _iob[0] = *stdout;

   _iob[0] = *stderr;

   return _iob;

}

也许这对你有帮助。我将 Shell32.lib 添加到我的链接器 --> 输入 --> 附加依赖项,它停止了这个错误。我从这个post发现了它:https://discourse.libsdl.org/t/windows-build-fails-with-missing-symbol-imp-commandlinetoargvw/27256/3