誰かが何を説明できますか
__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を使用しています。
Linker - > Input - > Additional DependenciesでSDL2.libとSDL2main.libにリンクし、VC++ディレクトリが正しいことを確認しました。
私はついにこれが起こっている理由を考え出しました!
ビジュアルスタジオ2015では、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])
そのため、以前のバージョンのVisual Studioでコンパイルされた.libファイルを使用すると、__ iob_funcが定義されなくなり、リンクエラーが発生します。
この問題を解決するには、{*stdin,*stdout,*stderr}
を含む配列を返す__iob_func()
を自分で定義してみてください。
Stdio関数に関する他のリンクエラー(私の場合はsprintf()
)については、リンカオプションにlegacy_stdio_definitions.libを追加できます。
IMOのMilanBabuškovにとって、これはまさに置換関数のようなものです:-)
FILE _iob[] = {*stdin, *stdout, *stderr};
extern "C" FILE * __cdecl __iob_func(void)
{
return _iob;
}
マイクロソフトはこれについて特別なメモを持っています( https://msdn.Microsoft.com/ja-jp/library/bb531344.aspx#BK_CRT ):
printfとscanf関数ファミリはインラインで定義されています。
Printfおよびscanf関数すべての定義は、stdio.h、conio.h、およびその他のCRTヘッダーにインラインで移動されました。適切なCRTヘッダーを含めずにこれらの関数をローカルに宣言したプログラムでは、これはリンカエラー(LNK2019、未解決の外部シンボル)につながる重大な変更です。可能であれば、CRTヘッダー(つまり#includeを追加)とインライン関数を含めるようにコードを更新する必要がありますが、これらのヘッダーファイルを含めるようにコードを変更したくない場合は、追加のソリューションを追加します。リンカ入力へのライブラリ、legacy_stdio_definitions.lib。
このライブラリをIDEのリンカ入力に追加するには、プロジェクトノードのコンテキストメニューを開き、「プロパティ」を選択してから「プロジェクトのプロパティ」ダイアログボックスで「リンカ」を選択し、区切りリスト。
プロジェクトが2015年より前のリリースのVisual C++でコンパイルされた静的ライブラリとリンクしている場合、リンカーは未解決の外部シンボルを報告することがあります。これらのエラーは、_ iob、_ iob_funcの内部stdio定義、または__ imp _ *の形式の特定のstdio関数の関連インポートを参照する可能性があります。プロジェクトをアップグレードするとき、すべての静的ライブラリを最新バージョンのVisual C++コンパイラおよびライブラリで再コンパイルすることをお勧めします。ライブラリがソースを入手できないサードパーティのライブラリである場合は、サードパーティから更新されたバイナリを要求するか、そのライブラリの使用法を古いライブラリでコンパイルした別のDLLにカプセル化する必要があります。 Visual C++コンパイラおよびライブラリのバージョン。
VS2015でも同じ問題がありました。私はVS2015でSDL2ソースをコンパイルすることによってそれを解決しました。
上で答えたように、正しい答えはVS2015ですべてをコンパイルすることですが、興味のために以下は私が問題を分析したものです。
このシンボルは、マイクロソフトがVS2015の一部として提供しているどの静的ライブラリでも定義されていないようですが、他のものが定義されているのでかなり特殊です。その理由を発見するためには、その関数の宣言と、さらに重要なことに、それがどのように使用されているかを調べる必要があります。
これは、Visual Studio 2008ヘッダーからの抜粋です。
_CRTIMP FILE * __cdecl __iob_func(void);
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])
そのため、関数の役割はFILEオブジェクトの配列の先頭を返すことです(ハンドルではなく、 "FILE *"がハンドル、FILEが重要な状態情報を格納する基礎となる不透明なデータ構造です)。この関数のユーザーは、さまざまなfscanf、fprintfスタイルの呼び出しに使用される3つのマクロstdin、stdout、およびstderrです。
では、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))
そのため、置換関数がファイルオブジェクトの配列のアドレスではなくファイルハンドルを返すようになり、マクロは識別番号を渡して関数を呼び出すように変更されました。
では、なぜ彼ら/私たちは互換性のあるAPIを提供できないのですか? Microsoftが__iob_funcを介した独自の実装に関しては、2つの重要なルールがあります。
上記のいずれかに変更があると、それに対してリンクされている既存のコンパイル済みコードが、そのAPIが呼び出された場合にひどく悪くなることを意味します。
FILEがどのように定義されたかを見てみましょう。
まず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 FILE定義:
typedef struct _iobuf
{
void* _Placeholder;
} FILE;
それでそれの核心がある:構造は形を変えた。 __iob_funcを参照する既存のコンパイル済みコードは、返されるデータがインデックス付け可能な配列であることと、その配列内で要素が同じ距離離れていることの両方に基づいています。
これらの行に沿って上記の回答で述べられている可能な解決策は、いくつかの理由で(呼び出されたとしても)うまくいかないでしょう。
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バイトになると予測します。それは返された配列のアドレスを取り、要素0に到達するために0バイトを追加します(それは大丈夫です)。それは64バイトを追加する必要があるということです(それはそれがVS2008ヘッダーでそれが見えた方法です)。実際、VS2008の逆アセンブルコードはこれを実証しています。
上記の解決策に関する二次的な問題は、それがFILE *ハンドルではなく、コピー FILE構造体(* stdin)の内容であるということです。そのため、どのVS2008コードもVS2015とは異なる基本構造を見ていることになります。構造体にポインタしか含まれていない場合は、これでうまくいく可能性がありますが、これは大きなリスクです。いずれにせよ、最初の号はこれを無関係にします。
私が夢見ることができた唯一のハックは、__ iob_funcが(返されたアドレスに追加されたオフセットに基づいて)彼らが探している実際のファイルハンドルを見つけるためにコールスタックを歩き、計算値を返すことです。正しい答えを出します。これはそれが聞こえるほど狂気のようなものですが、あなたの娯楽のために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;
}
理由はわかりませんが、
#ifdef main
#undef main
#endif
インクルードの後、メインの前に私の経験からそれを修正する必要があります。
この問題に対するより最近の解決策:より最近のsdl libsを使用してください。
" https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/?C=M;O=D "
それは32ビットライブラリにすぎませんが、彼らは問題を解決したようです。
リンクするとは、正しく機能しないことを意味します。 VS2012とVS2015のstdio.hを以下のように掘り下げてみました。ああ、あなたはそれが{stdin、stdout、stderr}のうちの1つのために働くべきであるかどうかを決定しなければなりません。
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);
}
私のアドバイスは、__ 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は、stdin
、stdout
、またはstderr
のうちの2つ以上では決して機能しないと考えています。
ライブラリが実際に使用している場合これらのストリームの1つ、クラッシュします。あなたのプログラムがlibにそれらを使用させない限り、決してあなたは知らないだろう。たとえば、CRCがPNGのメタデータで一致しない場合、png_default_error
はstderr
に書き込みます。 (通常クラッシュに値する問題ではありません)
結論:VS2012(Platform Toolset v110/v110_xp)ライブラリとVS2015 +ライブラリをstdin、stdout、stderrのいずれかまたは両方を使用している場合、それらを混在させることはできません。
解決策:未解決の__iob_func
シンボルを持つライブラリを現在のバージョンのVSおよびそれに対応するPlatform Toolsetで再コンパイルします。
VS2015プロジェクトのライブラリには、コンパイル済みのSDL2main.libとSDL.libを使用します。 https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225 .Zip
私は問題を解決することができた。
エラーの原因はこの行のコードで、SDLメインのソースコードにあります。
fprintf(stderr, "%s: %s\n", title, message);
だから私はその行のSDLメインのソースコードを編集することもしました。
fprintf("%s: %s\n", title, message);
そしてSDLmainをビルドし、SDL2ライブラリディレクトリ内の古いSDLmain.libを新しくビルドして編集したものにコピーして置き換えました。
その後、SDL2でプログラムを実行してもエラーメッセージが表示されず、コードもスムーズに実行されました。
これが後で私に噛み付くかどうかはわかりませんが、すべてが順調に進んでいます。
まだ上記のトリックがうまくいかなかった答えを探している人のために。静的リンクはこの問題を解決する方法です。以下のようにランタイムライブラリの設定を変更します。
Project properties --> C/C++ --> Code generation --> Runtime Library --> Multi-threaded Debug (/MTd) instead of /MDd
これは、msvcr10.dll(または類似のもの)の代わりにmsvcrt.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 Studio 6とそのコンパイラには必要ありません。したがって#ifdef。
私の場合、このエラーは私の試行によるもので、MSVCバージョンに依存するランタイムライブラリDLL(msvcr10.dllなど)への依存関係を削除したり、静的ランタイムライブラリを削除したりします。 。
だから私は/ NODEFAULTLIBリンカスイッチ、私が作った "msvcrt-light.lib"(あなたが必要なときそれのためにグーグル)、そしてmainCRTStartup()
/WinMainCRTStartup()
エントリーを使う。
それはVisual Studio 2015以降の私見ですので、私は古いコンパイラにこだわりました。
ただし、シンボル_ NO_CRT_STDIO_INLINEを定義すると、すべての面倒な作業がなくなり、単純な "Hello World"アプリケーションも3 KB小さくなり、異常なDLLに依存しなくなります。 Visual Studio 2017でテスト済み。
この既に豊富なスレッドをもっと混乱させるために、私はたまたま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)よりも多くの問題が発生するため、これは奇妙なことです...しかしそれが他に役立つ場合に備えて、私はそれをそこに置きます。
私は以下の機能でこの問題を解決しました。私はVisual Studio 2019を使っています。
FILE* __cdecl __iob_func(void)
{
FILE _iob[] = { *stdin, *stdout, *stderr };
return _iob;
}
stdinマクロ定義関数呼び出しのため、 "* stdin"式はグローバル配列初期化子を使用できません。しかし、ローカル配列の初期化は可能です。すみません、私は英語が苦手です。