ビルドの最後にfatal error LNK1120: 2件の未解決の外部参照 と出て、exeができません。件数だけ言われても、どこを直せばいいのかは分かりませんよね。
見るべきは、その1つ上に並んでいるLNK2019とLNK2001の行です。出力ウィンドウを少し上へスクロールしてみてください。
LNK1120の意味
公式ドキュメントにも、そう書いてあります。「エラーLNK1120は、現在のリンク内の未解決の外部シンボル エラーの数を報告します。」しかも「このエラーを修正する必要はありません。」とまで書いてあります。
未解決のシンボルは、まずLNK2001かLNK2019で報告されます。LNK1120が出るのはそのあとで、数を数えて見せているだけです。上の行を片づけていけば、LNK1120も勝手に消えます。
エラー行に出る2つの名前
宣言だけを書いて、定義をどこにも置かない関数と変数を用意しました。それをmainから参照してリンクすると、Visual Studio 2026 / Debug / x64ではこうなりました。長いパスが並ぶと読みにくいので、行末のプロジェクトのパスは省いてあります。ファイル名も短いものに置き換えました。
main.obj : error LNK2019: 未解決の外部シンボル "void __cdecl ReportShortage(int)" (?ReportShortage@@YAXH@Z) が関数 main で参照されました
main.obj : error LNK2001: 外部シンボル "int g_nStockThreshold" (?g_nStockThreshold@@3HA) は未解決です
MyApp.exe : fatal error LNK1120: 2 件の未解決の外部参照
行頭にあるのは、その参照を持っている .objです。関数のほうはLNK2019で、参照元のmainまで名指しされています。変数のほうはLNK2001で、参照元は出ていません。同じファイルの同じ関数から参照していても、行の形は変わります。
どちらの行にも、同じシンボルの名前が2つ並んでいます。”void __cdecl ReportShortage(int)” がソースに書いた形で、かっこの中の ?ReportShortage@@YAXH@Zが装飾名です。公式ドキュメントでも「”フレンドリ名” (ソース コードで使用される名前) と装飾名 (かっこ内) の両方が表示されます。」と説明されています。
リンカーが探しているのは、かっこの中のほうです。暗号のように見えますが、中身が読めなくても困りません。公式も「装飾名の解釈方法を理解している必要はありません。 検索して、他の装飾名と比較することができます。」と書いています。他の装飾名と見比べられれば十分、ということです。なお、リンカーが装飾名しか報告できない場合もあります。
装飾名の正体
装飾名は、関数名をそのまま写したものではありません。公式の説明では「関数のシンボル名では、その戻り値の型、パラメーターの型、スコープ、呼び出し規則などがエンコードされます。」となっています。つまり、宣言と定義がここで食い違うと、リンク エラーが起きることがあります。
では、どこを変えると名前が変わるのでしょうか。中身が同じ関数を6本用意して、宣言だけを1か所ずつ変えてみました。それぞれの装飾名を並べたのが次の画面です。

| 宣言で変えたところ | 装飾名に出た違い(実測) |
| extern “C” を付けた | 装飾されず、関数名がそのまま入る |
| 引数をLPCSTR / LPCWSTRに | PEBDとPEB_Wに分かれる |
| 引数をLPCTSTRに | このUnicode構成ではPEB_W側になった |
| __stdcallを付けた | x64では既定と同じ符号のまま変わらなかった |
最後の行は意外かもしれません。でも仕様どおりです。__stdcallの公式ページに「ARMおよびx64プロセッサでは、__stdcallはコンパイラによって受け入れられるか無視されます。」とあります。x64では、__cdeclと __stdcallを書き分けても装飾名は変わりません。この2つの取り違えが名前に出るのはx86のときです。ただし __vectorcallのようにx64でも効く規約はあるので、呼び出し規約そのものを候補から外さないでください。
LPCTSTRが構成で変わるのは、型の定義がそうなっているからです。Windowsデータ型の表はLPCTSTRを「UNICODEが定義 場合はLPCWSTR、それ以外の場合はLPCSTR」と書いています。C++ の関数の引数にLPCTSTRを使っていると、UNICODEの有無が違うライブラリを混ぜたときに装飾名がそろいません。いっぽうextern “C” の関数名には、引数の型が入りません。ですから、この食い違いは名前には出てきません。
名前ごとに見る場所が変わる
フレンドリ名を読めば、それが誰の関数かは分かります。そこから先は、名前の出どころで分かれます。
| 未解決になった名前 | 最初に見る場所 |
| 自分で書いた関数・変数 | 定義を書いた .cppがプロジェクトのビルド対象に入っているか |
| ライブラリの関数 | [リンカー] > [入力] > [追加の依存ファイル] にその .libがあるか |
| クラスのstaticメンバー | どこか1つの .cppに定義を書いたか |
| 受け取った .libの中の名前 | その .libがx64とx86のどちら用にビルドされているか |
| 名前が惜しいところで食い違う | 引数の型・extern “C”・呼び出し規約 |
公式が「考えられる原因」の先頭に置いているのも、この表の1行目と2行目にあたる原因です。「シンボルの定義を含むソース ファイルがコンパイルされていません」「シンボルの定義を含むオブジェクト ファイルまたはライブラリがリンクされていない」の順で並んでいます。アーキテクチャについても「コードにリンクされているライブラリとオブジェクト ファイルは、コードと同じアーキテクチャ用にコンパイルする必要があります。」と書かれています。
さきほどのエラー行を出した宣言は、ヘッダーに置いたこの2行だけです。定義はどこにもありません。
void ReportShortage(int nCount);
extern int g_nStockThreshold;
これでもコンパイルは通ります。公式の説明どおり、コンパイラは宣言されていないシンボルは見つけられても、定義されていないシンボルは見つけられません。定義が別のソース ファイルやライブラリにあるかもしれないからです。止まるのはリンクの段階です。LNK1120の「2件」は、この2つの宣言に対応していました。
それでも通らないときに使うコマンド
定義の入ったファイルがビルドに含まれているかは、/VERBOSEリンカー オプションで見られます。公式の説明は「リンカーがどのファイルを参照しているかを確認できます」です。名前そのものを調べるならDUMPBINの /SYMBOLS、装飾名を元の形に戻すならUNDNAMEです。
/SYMBOLSの出力は、名前があるかどうかだけでは読めません。公式は「3番目の列にSECTxが含まれている場合は、オブジェクト ファイルのそのセクションにシンボルが定義されます。 ただし、UNDEFが表示されている場合は、そのオブジェクトで定義されていないため、他の場所で解決する必要があります。」と説明しています。5番目の列(Static / External)も見ます。Staticのシンボルは外から参照できません。名前が一致していてもリンクは通りません。なお /GLでビルドしたファイルには /SYMBOLSを使えません。
ここまでは、リンカーが定義を見つけられないときの話でした。ファイルそのものを開けないLNK1104は、原因の層が違います。直し方はLNK1104 / LNK2019 / LNK2001の記事に分けてあります。定義が多すぎるLNK2005は逆向きの問題で、LNK2005 / LNK1169の記事で扱っています。
確かめた構成は、出力ごとに違います。上のエラー行は、MFCを使わないコンソール アプリで出しました。装飾名の一覧のほうはMFC共有DLLのダイアログ アプリです。共通なのはVisual Studio 2026 / Debug / x64 / Unicodeです。ビルドに使ったのはMSBuild 18.9.1.35102、プラットフォーム ツールセットはv145でした。装飾名の中身は、この版と構成で変わります。
まとめ
- 件数だけを読んで、出力ウィンドウを上へスクロールする。直す行はLNK2019とLNK2001のほう。
- 行に並んだ名前のうち、かっこの中の装飾名を見る。リンカーが探しているのはこちら。
- フレンドリ名で出どころを決める。自分の関数なら定義とビルド対象、ライブラリの関数なら [追加の依存ファイル] とアーキテクチャ。
- 名前が惜しいところで食い違うなら、引数の型とextern “C” を見る。__cdeclと __stdcallの取り違えはx64では名前に出ない。
