自習室

こもります

Visual c++ の同一ソリューション内ライブラリ参照と、ライブラリプロジェクトへの静的リンクライブラリのリンクの仕方。

概要

Visual Studio はエディターとして超優秀だけど、ビルド時の「追加の依存ファイル(静的リンクライブラリなど)」の指定や、インクルードパスの指定をする「プロジェクトプロパティ」がとてもわかりにくいし、それをわかりやすくしようとした結果生まれた .props ファイルによるプロジェクトプロパティのファイル定義がまたややこしいってんで、なにかちょっと不慣れなことをしようとしたり、古いOSSを新しいVSで使おうとすると痛い目に遭いがち。

今回はそれげな話の一つとして、実務で遭遇した以下の2件についてメモを残しておきます。

  1. スタティックリンクライブラリを作るプロジェクト(PrjS1)と、それを使って動く .exe を作るプロジェクト(PrjE)、の2プロジェクトを持つVisual Studio の一つのソリューション(Sln1)において、PrjEがPrjS1.libを使うケース
  2. 単独のスタティックリンクライブラリを作るプロジェクト(Sln2/PrjS2) を用意し、PrjS1.libが PrjS2.lib を使うケース

図にまとめるとこんな感じ

f:id:AMANE:20190606222454p:plain

それそれどのように "参照" やリンクを行うのか、についてまとめておこうと思います。

前提知識

私も記憶喪失になりがちなので、大事な前提知識として、DLLやLIBってなんやねん?を詳細に説明して下さっている記事へのリンクを貼らせていただきます。

kamino.hatenablog.com

(1) ソリューション内の参照

ソリューション内のプロジェクト参照とは何か。

こちらに記載があります。

プロジェクト内の参照の管理 - Visual Studio | Microsoft Docs

Visual Studio は、プロジェクトへのパスが指定されると、アセンブリを見つけることができます。

とありますが、.lib ファイルをビルドするプロジェクトを参照した場合は、結果の .lib ファイルを見つけることが出来る、と言う意味ですね。

ソリューション内の別のプロジェクトへの参照の仕方

  • プロジェクトを右クリック → 追加 → 参照 → ソリューション内の .lib ファイルを作るプロジェクト名にチェック
    • 参照右クリック→参照の追加、でも同じ

f:id:AMANE:20190606224220p:plain

似たような項目で、プロジェクトの依存関係というのがあります。

  • ソリューションを右クリック → プロパティ → 共通プロパティ → プロジェクトの依存関係

f:id:AMANE:20190606224330p:plain

こちらは、同一ソリューション内のプロジェクト間のビルド順を定義するための設定です。試しに PrjEPrjS1 に依存するよう指定すると、ビルド順こそ最適化されますが、 PrjS1 中のコードの header を持ってきて使おうとすると、 未解決の外部シンボルが参照されました のようなエラーが出ます。これは無論、 PrjS1.lib がリンクできていないからです。

方法: プロジェクトの依存関係を作成および削除する - Visual Studio | Microsoft Docs

逆に、先にPrjEからPrjS1を参照すると、自動的に PrjEPrjS1を依存することもプロパティに追記されます。

ソリューションの構成とプラットフォームの自動反映

このようにプロジェクト参照で引いている .lib は、構成(Debug/Release)やプラットフォーム(x86/x64) をソリューション全体で変えたとしても、利用する側の PrjE に対し、適切な構成・プラットフォームで作られた PrjS1.lib を渡してくれます。

それを確認するために、以下の手順で意地悪をしてみます。

  • 一旦ソリューションをクリーン
  • Debug/x86PrjS1 をプロジェクトのみビルド
  • Debug/x86PrjE をプロジェクトのみビルド → 正しくビルドされる( PrjS1.libデバッグビルド物を引けている)
  • Release/x86PrjE を再度プロジェクトのみビルド → ビルド完了出来ない(この時点ではPrjS1.lib** のリリースビルドが存在しないため)
  • Release/x86PrjS1.lib をプロジェクトのみビルド、Debug/x86 でビルドした PrjS1.lib を削除
  • Release/x86PrjE を再度プロジェクトのみビルド → 正しくビルドされる

その間、プロジェクトプロパティ類は一切編集しませんでしたがこのように、適切な .lib ファイルを引いてくれることがわかります。

ちなみに細かい話ですが、ソリューションエクスプローラ上で PrjE / 参照 / PrjS1 を選択してプロパティウィンドウを見ると、構成が Debug の時に Release\PrjS1.lib が完全パスとして指定されていたり、その逆だったりすることがあります。これはおそらく Visual Studio の表示上のバグで、実際、Visual Studio を再起動すると、正しい .lib ファイルのパスが記載されています。

f:id:AMANE:20190606224523p:plain

(2) ソリューション外のビルド物である .lib ファイルを、自分のライブラリ(.libを出力する)プロジェクトに追加する

他所のリリース物を自分のプロジェクトに取り込んで使う場合、こちらの方が良くあると思います。特に、 PrjE(.exeを作るプロジェクト)PrjS2(.lib) をリンクする場合は、 プロジェクトプロパティ / リンカー / 追加のライブラリディレクトリ入力 / 追加の依存ファイル(.libファイル) を指定する、という「VSおきまりの」手続になります。

qiita.com

今回やりたいのは、静的リンクライブラリを出力するプロジェクトに外部の静的リンクライブラリをリンクする、ことです。基本的には上記と同じなのですが、 .exe を出力するプロジェクトと .lib を出力するプロジェクトで、若干プロジェクトプロパティの構成が異なっています。 .h ファイルのインクルードに関しては、 .exe のプロジェクトの場合と同様に、 C/C++ / 全般 / 追加のインクルードディレクトリ に使いたい .h ファイルのあるディレクトリを指定します。

ライブラリファイルの指定は、ライブラリアン / 全般 タブ中の 追加の依存ファイル追加のライブラリディレクトリ を、それぞれ構成・プラットフォーム毎に正しく設定する必要があります。これにより、構成・プラットフォームを変えても、PrjS1 はそれ毎に正しい .lib ファイルをビルドしてくれ、最終的に PrjE は正しくビルドされることになります。

f:id:AMANE:20190606231518p:plain

所感

昔ながらの静的リンクライブラリのロードの仕方として、 #pragma comment(lib, "filename.lib") 式もありますが、その他の dll ファイルの置き場などすべてプロジェクトのプロパティから指定しているので、.lib` ファイルもプロジェクトプロパティの指定でやりきりたいなぁと私は考えています。コード中に書く方がマルチプラットフォーム開発では良いことも多いかも知れませんが、それはそのときに。

[VC++] リンクするライブラリファイルをソースコード内に記述する | あみだがみねのもろもろ備忘録

これまでかなり適当にやって、何となくビルドできればおっけーって感じで通ってきてた内容を一通り整理できて良かったです。