VisualStudioでC/C++ビルド時,stdio.hやkernel32.libなど基本的なモジュールが見つからないときの対処法
※2016/12/16全面刷新
※私の認識に誤りがあり,どうやらVisualStudioのincludeやlibのパス設定方法がまずかったようです。
※またパス展開用のマクロ名が,古いVisualStudioのもの(つまり移行時に発生しやすい?)だった場合も
※問題が起きる気がします。。。
今回の環境
Windows XP(32-bit) + Visual Studio 2005 Professionalから
Windows 7(64-bit) + Visual Studio 2010 expressへ移行テストで問題発生。
Windows 10(64-bit) + Visual Studio 2015 Community導入時にも同様の問題が発生しました。
問題 - hello, worldできない!
10年もプログラム組んでてprintfも知らんのか!という話ではなく。
↓の何の変哲もないプログラムをコンパイルすると…。
#include <stdio.h> int main() { printf("hello, world\n"); return 0; }
以下のように,あって当たり前のヘッダやライブラリが見つけられず,コンパイル/リンクに失敗します。
include ファイルを開けません。'stdio.h': No such file or directory fatal error LNK1104: ファイル 'kernel32.lib' を開くことができません。
この問題,VisualStudioのincludeやlibのパスに誤りがあり,stdio.hやkernel32.libが見つけられていないのです。
つまり,stdio.hやkernel32.libの場所を正しくVisualStudioに教えてあげれば,この問題は解決できるというわけです。
Visual Studioが検索するパス
そもそもVisual Studioがヘッダやライブラリを探すとき,どこを見に行くのでしょうか?
この「どこを見るか」の設定は,Visual Studio 2010以降はプロジェクト毎に設定することになっています。
ご自分が開発しているプロジェクトを右クリック→[プロパティ]→[構成プロパティ]→[VC++ ディレクトリ]と
辿ってみてください。(以下はVisual Studio 2015 Communityの例です)
上記の[インクルードディレクトリ]を見てください。
$(VC_IncludePath);$(WindowsSDK_IncludePath);
と書いてありますが,これがいわゆるパス展開用のマクロで,
実際には「C:\Program Files (x86)\Visual Studio..」といったパスに置換されます。
複数パスがある場合はセミコロン(;)で区切られています。
では,このマクロが実際どのような値に置換されるのかを見てみましょう。
[インクルードディレクトリ]の行の右にマウスをオーバーさせると▼のようなアイコンが表示されますので,
それをクリック,次に出てくる「<編集>」をクリックしてください。以下のダイアログが表示されます。
[評価された値]を見てください(以下にテキストを記載)。これが実際に置換されたパスで,
VisualStudioはこれらのパスからヘッダファイルを探しているのです。
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt C:\Program Files (x86)\Windows Kits\8.1\Include\um C:\Program Files (x86)\Windows Kits\8.1\Include\shared C:\Program Files (x86)\Windows Kits\8.1\Include\winrt
対処法
さて,私の環境ではstdio.hが見つからないと怒られていました。
正しい環境のパスと比べてどこが違うのかを見てみましょう。
■私のプロジェクト
C:\Program Files (x86)\Windows Kits\8.1\Include\um C:\Program Files (x86)\Windows Kits\8.1\Include\shared C:\Program Files (x86)\Windows Kits\8.1\Include\winrt C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include C:\Program Files (x86)\Windows Kits\8.1\include C:\Program Files (x86)\Windows Kits\8.1\\include C:\Lib\libogg\libogg-1.3.0\include C:\Lib\libvorbis\libvorbis-1.3.3\include C:\Lib\boost_1_53_0 C:\Lib\Microsoft DirectX SDK (June 2010)\Include C:\Lib\lua-5.1.5\include C:\Lib\luabind-0.9.1
■正しいプロジェクト
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt ←stdio.hはここにある C:\Program Files (x86)\Windows Kits\8.1\Include\um C:\Program Files (x86)\Windows Kits\8.1\Include\shared C:\Program Files (x86)\Windows Kits\8.1\Include\winrt
私のプロジェクトでは色々と外部ライブラリのパスを追加していますが,
肝心のstdio.hがある
C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
が存在していません。
右のペインから,このパスは「$(VC_IncludePath)」に含まれていることがわかるので,
このマクロを[インクルードディレクトリ]追加することにより,無事stdio.hを見つけられるようになりました。
散々ひっぱりましたが,解決方法はたったこれだけです。
ライブラリも同様の方法で解決可能でした。
なんでこんなことになっていたのか(ここは興味ある人だけ読んでください。興味ない人は↓の「結論」へGO)
そもそも,なぜ以下のパスがインクルードディレクトリに含まれていなかったのでしょうか?
C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
以前Visual Studio 2005から2010に移行したとき,
私のプロジェクトのディレクトリ設定は以下のようになっていました。
include | C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include |
ライブラリ | C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Lib |
必要なパスが全然足りていません。
しかもマクロを使っておらず,パスがベタ書きでした。
Visual Studioインストールディレクトリ等,自前で用意するライブラリ以外はマクロを使いたいところです。
そこで私は適当に2010でプロジェクトを新規作成し,そこに設定されている
[インクルートディレクトリ]と[ライブラリディレクトリ]をコピーして使うことにしました。
種別 | パス |
インクルードディレクトリ | $(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; |
ライブラリディレクトリ | $(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib |
さらにDirectX等,自前で用意した外部ライブラリのパスをくっつけます。
これで無事ビルドを通すことができました。
パスの設定をVisual Studio全体の環境に設定する
実はここまでに説明したパスの設定はVisual Studio 2008以前はプロジェクト毎ではなく,
全プロジェクト共通で使うものでした。
2010に移行した自分は「プロジェクト毎にパスを設定する」ことに違和感を覚えたというか
激しくめんどくさいと感じていたため,以前と同じようにVisual Studio全体にパスを適用できないか,
その方法を模索しました。
※いま考えると,予期せぬ場所のヘッダやライブラリを見てしまうことを防止するため,
やはりプロジェクト毎にフォルダを設定する現仕様の方が安全だと思います。
色々調べた結果,以下のファイルにincludeやlibディレクトリのパスを設定すれば,
Visual Studio全体にパスが適用されることがわかりました。
<Windows7以降> C:\Users\(ユーザー名)\AppData\Local\Microsoft\MSBuild\v4.0\Microsoft.Cpp.Win32.user.props <XP> C:\Documents and Settings\(ユーザー名)\Local Settings\Application Data\Microsoft\MSBuild\v4.0\Microsoft.Cpp.x64.user.props
というわけで,ファイル内容は以下のように設定しました。
<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include</IncludePath> <LibraryPath>$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib</LibraryPath> </PropertyGroup> </Project>
これで,Visual Studioで新規にプロジェクトを作成すると,ここに設定したinclude/libのパスが
デフォルトで[インクルードディレクトリ]と[ライブラリディレクトリ]に設定されることになります。
あぁよかったよかった一件落着…とはならず,これがまずかった。
Visual Studio 2015 Communityでデフォルトで設定されているincludeディレクトリを見てみましょう。
$(VC_IncludePath);$(WindowsSDK_IncludePath);
です。自分が設定したパスのマクロと全然違います。また自分が書いたマクロは,マクロ一覧に定義がなく
おそらくVisual Studio 2015 Communityでは空文字に置き換わってしまっていたのでしょう…。
つまり,一度上記ファイルに書き込んでしまうとVisual Studioをバージョンアップしようが
書き込んだパスが設定されてしまい,古いマクロを使っていたり新しく設定されるはずの
マクロが設定されなかったりと問題なるのです。
これが、今回こんなことなってしまった原因でした。
そのため,上記ファイルにはinclude/libパスを書かないようにしましょう。
パスを書かなければ,以下のようなVisual Studioがそのバージョンで正しくパス展開できるマクロを設定してくれるはずです。
$(VC_IncludePath);$(WindowsSDK_IncludePath);
その後,外部ライブラリがある場合はプロジェクト毎にそのライブラリのinclude/libディレクトリを
設定すればいいです。これが正しいやり方でしょう。
結論
長々と書きましたが,以下(1)(2)を守れば題名の問題は解決できます!
(1) 以下のファイルにはinclude/libディレクトリを書かない
<Windows7以降> C:\Users\(ユーザー名)\AppData\Local\Microsoft\MSBuild\v4.0\Microsoft.Cpp.Win32.user.props <XP> C:\Documents and Settings\(ユーザー名)\Local Settings\Application Data\Microsoft\MSBuild\v4.0\Microsoft.Cpp.x64.user.props
(2) 外部ライブラリ(DirectX等)を使う場合は,プロジェクト毎にinclude/libディレクトリを設定する
上記を守って,思いっきりhello, world!と叫んじゃってください(^_^)/