ぷるぷるの雑記

低レイヤーがんばるぞいなブログ. 記事のご利用は自己責任で.

VC++のVisual Studioにおけるビルド

Visual Studio(MSBuild)でVC++をビルドするときの箇条書きメモ. MyProjectというウインドウを持つMFCプロジェクトのx64ビルドを想定.

実行環境

項目 説明
OS Windows11
Visual Studio 2017
ビルドターゲット x64

関連するパス

パス 主なファイル 説明
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64 cl.exe, link.exe, nmake.exe, dumpbin.exe バイナリツール
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include iostream, algorithm VC++用ヘッダー
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\lib\x64 msvcrt.lib, legacy_stdio_definitions.lib VC++用ライブラリ
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\atlmfc\include afx.h, about_.rc MFCのヘッダー, デフォルトのリソース
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64 mfc140.lib MFCのインポートファイル
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools MSBuild.exe, VsDevCmd.bat MSBuild及びパスを指定するためのbat
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE devenv.exe Visual Studio 本体
C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64 rc.exe Windows開発用のツール
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0 winsock.h, WinBase.h C言語によるWinアプリケーション用ヘッダーファイル
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64 kernel32.LIB, d3d12.lib C言語によるWinアプリケーション用インポートファイル
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\ucrt\x64 libucrt.lib, ucrt.lib C言語によるWinアプリケーション用インポートファイル
C:\Windows\System32 kernel32.dll, msvcp140.dll, mfc140.dll, D3D12.dll, curl.exe 64bit用ランタイム, 基本コマンド

ランタイムとSDK

ざっくり言うと、ランタイムはdllで実行時に必要. SDKはヘッダー、インポートライブラリあるいはスタティックライブラリでビルド時に必要. いかなる時も必要なランタイムと、プロジェクトの種類によっては必要なランタイムがある. ランタイムが欲しい場合は再配布可能パッケージを取得すればよい.

主要なランタイム 説明
kernel32.dll よくみるけどなにしてるんだろう
ucrtbase.dll よくみるけどなにしてるんだろう
vcruntime140.dll Visual Studio2015以降のVC++ビルドツールで作ったアプリの実行に必要
mfc140.dll Visual Studio2015以降のVC++ビルドツールで作った動的MFCアプリの実行に必要

ランタイムをざっくり分けると

  • ほぼ無条件で必要なもの...kernel32.dllなど
  • VC++で作った場合に必要なもの...msvcr140.dllなど
  • MFCで作った場合に必要なもの...mfc140.dllなど

とがある.

ファイルの配置規則

  • ヘッダーファイルはターゲットごとに分けずに配置
  • インポートファイルはターゲットごとにフォルダを分けて配置
  • インポートファイルとdllファイルは一対一対応とは限らない
  • VC++のビルドツールはバージョンごとに分かれているが、ビルドツールを追加でインストールしない限りは1つしかない
  • WinSDKのツールやライブラリはバージョンごとに分かれている. 一部フォルダはWinSDKインストール前から存在する

ファイルの命名規則

  • dが拡張子の前についているファイルはデバッグビルド用
  • uが拡張子の前についているファイルはUnicode対応
  • LIB〇〇.LIBというファイルはインポートライブラリではなくスタティックライブラリ
  • C:\Windows\System32は64bit用(紛らわしい!). C:\Windows\SysWOW64は32bit用(紛らわしい!).
  • ランタイムは32bitも64bitも同名にしている. kernel32.dllにも64bit用のものと32bit用のものがある.

ビルドの流れ

CL.exeによるコンパイル → rc.exeによるリソースコンパイル → link.exeによるリンク

詳細

  1. ビルドターゲットに対応するコマンドプロンプトを開く
  2. msbuild.batが呼び出され、MSBuild.exeへのパスが追加される.
  3. winsdk.batが呼び出され「WindowsSDKVersion」が自動的に指定される(おそらくPCに入っているものの中で最新のもの)
  4. vcvars.batによりVCビルドツールのバージョン、WinSDKのバージョン、ターゲットに対応したインクルードフォルダ、ライブラリフォルダへのパスを環境変数に追加.
  5. 1で開いたコマンドプロンプトMSBuild <MyProjectへのパス>\MyProject.vcxproj と叩く.
  6. MyProject.vcxproj内のVCビルドツールのバージョンとWinSDKのバージョンで環境変数を上書きしたうえでビルド開始.

IDE上からビルドするとMSBuildのオプションを指定したうえでビルドできる(後述)

インポートライブラリ

vcvars.batのおかげでもとからいくつかのディレクトリへのパスが通っている. 例えば、IDE上で以下のように書いてもリンクエラーにならないのは「C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\atlmfc\lib\x64」や「C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64」などへのパスがvcvars.batのおかげで通っているから.

/******************************************/
/*           afx.hからの抜粋              */
/*****************************************/

#ifndef _AFXDLL
    #ifdef _AFX_NO_MFC_CONTROLS_IN_DIALOGS
        #ifdef _DEBUG
            #pragma comment(lib, "afxnmcdd.lib")
        #else
            #pragma comment(lib, "afxnmcd.lib")
        #endif
        #pragma comment(linker, "/include:__afxNoMFCControlSupportInDialogs")
        #pragma comment(linker, "/include:__afxNoMFCControlContainerInDialogs")
    #endif
    #ifndef _UNICODE
        #ifdef _DEBUG
            #pragma comment(lib, "nafxcwd.lib")
        #else
            #pragma comment(lib, "nafxcw.lib")
        #endif
    #else
        #ifdef _DEBUG
            #pragma comment(lib, "uafxcwd.lib")
        #else
            #pragma comment(lib, "uafxcw.lib")
        #endif
    #endif
#else
    #ifndef _UNICODE
        #ifdef _DEBUG
            #pragma comment(lib, "mfc" _MFC_FILENAME_VER "d.lib")
            #pragma comment(lib, "mfcs" _MFC_FILENAME_VER "d.lib")
        #else
            #pragma comment(lib, "mfc" _MFC_FILENAME_VER ".lib")
            #pragma comment(lib, "mfcs" _MFC_FILENAME_VER ".lib")
        #endif
    #else
        #ifdef _DEBUG
            #pragma comment(lib, "mfc" _MFC_FILENAME_VER "ud.lib")
            #pragma comment(lib, "mfcs" _MFC_FILENAME_VER "ud.lib")
        #else
            #pragma comment(lib, "mfc" _MFC_FILENAME_VER "u.lib")
            #pragma comment(lib, "mfcs" _MFC_FILENAME_VER "u.lib")
        #endif
    #endif
#endif

#ifdef _DLL
    #if defined(_DEBUG)
        #pragma comment(lib, "msvcrtd.lib")
    #else
        #pragma comment(lib, "msvcrt.lib")
    #endif
#else
    #if defined(_DEBUG)
        #pragma comment(lib, "libcmtd.lib")
    #else
        #pragma comment(lib, "libcmt.lib")
    #endif
#endif

#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "msimg32.lib")
#pragma comment(lib, "comdlg32.lib")
#pragma comment(lib, "winspool.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "uxtheme.lib")
#pragma comment(lib, "windowscodecs.lib")

追加のディレクト

vcvars.batで追加されたディレクトリ以外へのパスを追加する場合は、プロジェクトプロパティから「追加のインクルードディレクトリ」と「追加のライブラリディレクトリ」の項目にディレクトリへのパスを指定する. ライブラリに関してはプロジェクトプロパティの「追加の依存ファイル」に指定してもよいし、#pragma comment(lib, "libName") をソースファイル内に記述すればよい. おそらく、プロプロセスかどこかで依存ファイルを自動的に作成してくれる. vcvars.batはあくまでパスを指定するだけなので、ヘッダ内で #pragma comment(lib, "libName")を記述することでデフォルトの依存関係を指定している.

ログ

MyProject.tlogフォルダにCL、rc、linkのログが出力される. 特にCLとlinkは参照したファイルがすべてフルパスで出力されているので必見.

MSBuildのオプション

MSBuildコマンドラインからオプションなしで実行すると、x64 Debug ビルドになってしまう. /property (/p for short)オプションを指定するとコマンドラインからターゲットを指定できる.

MSBuild MyProject.vcxproj /p:Configutation=Release                        # x64 Release Build
MSBuild MyProject.vcxproj /p:Configutation=Release /p:Platform=Win32      # x86 Release Build

NuGetとの相性

.NET Frameworkの場合NuGetからインストールしたdllへのパスや依存関係が自動的にプロジェクトファイルに追加される. VC++の場合はすべて手動で設定する必要がある.

参考

learn.microsoft.com

atmarkit.itmedia.co.jp