マシンを更新したついでに開発環境も一気にバージョンアップすることにしました。といっても完全に最新にはしませんでした。まあ、一応WinXPでプログラムを動作させるなどを考えるとあまりにも最新にしすぎると問題が出る可能性がある、ということで一つ手前であるVS2010にアップすることにしました。そのため、話題的に最新ではないです。
しかしながら、これだけバージョンが上がるといろいろと動作変更やコンパイル時に問題が出たりして・・・。(自分が開発してきた各プロジェクトを)この環境を整合させるのに3日間ほどかかってしまいました。一応すべてバージョンアップが終わってある程度の動作チェックができたのでよしとしています。先に書いておくと、以降開発環境はVS2010を基準としていきます。MovieLayerPlayerやDirectShow Extend Filter Libraryも更新版を作成しましたのでもう少し動作チェックができれば公開する予定です。WindowModePatchも動作は確認したのですがあるプログラムに対する対応コードを組んでいるときに環境が更新されたのでその部分ができたら公開する予定となっています。
インストールそのものは何も変わりなし
というか以前にSliverlightのプログラムを構築したときに一時的にVS2010を使っていたのでそこまで戸惑うようなことはなかったですね。普通にインストールして終わりです。ただ、以前と違ってヘルプを完全にオフラインで参照することができなくなっていたり、コンパイルエラーの行を選択してF1を押しても対象のコンパイルエラーに関するヘルプが参照されなかったりとヘルプの機能は大幅に劣化しているという印象が第一ですね。で、最新の環境に持って行くなら、ということでいつもの通り最新のWindowsSDKを入れて環境を適応させ・・・ようとしていろいろと大変だったのですよ。
ライブラリやインクルードディレクトリの参照はVisualStudioの設定ではなく別のファイルに分離されている
まず戸惑った点第一。WindowsSDKや、古いですがDirectXSDK、また自分で作ったライブラリやインクルードファイルをVisualStudioから参照させるためにはライブラリディレクトリやインクルードディレクトリをプロジェクトに教えなければ使えません。その設定がVisualStudio本体から別ファイルに移動させられているのでそちらを編集することになります。ファイル位置は(自分のホームディレクトリ)\AppData\Local\Microsoft\MSBuild以降(にバージョン番号などがある)のファイルとなります。x86であれば「Microsoft.Cpp.Win32.user.props」、x86-64であれば「Microsoft.Cpp.x64.user.props」が対応します。インストールされた直後はほとんど何も設定されていないので、まずは設定しやすいように(Microsoft.Cpp.Win32.user.propsを例にすると)
<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <ExecutablePath>$(ExecutablePath)</ExecutablePath> <IncludePath>$(IncludePath)</IncludePath> <ReferencePath>$(ReferencePath)</ReferencePath> <LibraryPath>$(LibraryPath)</LibraryPath> <SourcePath>$(SourcePath)</SourcePath> <ExcludePath>$(ExcludePath)</ExcludePath> </PropertyGroup> </Project>
というようにPropertyGroupで各ディレクトリ情報のデフォルトを設定した上で必要なパスを書き加えていく必要があります。SDKをインストールした場合はIncludePathおよびLibraryPathにそのパス情報を追加すればC++系のどのプロジェクトでもライブラリが参照されるようになります。注意が必要なのはExecutablePathに追加する場合にはとても注意が必要である、ということです。この話は次の項で。
Trackerのエラーって何だよ~
VS2010でコンパイルすると環境によってはTrackerという機構がエラーを返すことがあります。特にx86のビルド環境からプロジェクトを継承してx64のビルド環境を作成し、IDEでビルドを行うとTRK0002を出したりすることがあって大変。どうも環境変数の引き継ぎなどでパスが狂うとこんなことになるようでこの辺はほかのサイトでもいろいろと書いてあるので詳しくはそちらに譲ります。で、私の場合はほかのサイトに書いてあった原因ではなく、なんとSDKの実行ファイルを参照するためにExecutablePathに追加してあった(しかもデフォルトの環境変数より前)ためにTRK0002エラーが発生するという何ともよくわからない原因でした。結局実行ファイル群はデフォルトの環境変数の後ろに回してとりあえず一段落。
古い環境を移行していく場合は最新SDKのライブラリやインクルードファイルが使えないことも
で、次にはまった項目がこちら。最新のWindowsSDKにはDirectShowのライブラリ群であるbaseclassesが同梱されていません。というか、そもそもサンプルファイルがインストールされないのでネットワーク上からとってこないとだめなのですが、そのサンプル群にbaseclassesがないので撃沈です。
仕方が無いので古いSDK(WindowsSDK for 7)からbaseclassesを取り出してコンパイル、そしてそのライブラリを使おうと思ったのですがここで第二弾が。通常baseclassesを使うときはbaseclassesのディレクトリをデフォルトのインクルードディレクトリに追加するのですが、なんと最新のWindowsSDKが持っているインクルードファイルとbaseclassesのインクルードファイルのファイル名が同じであるために優先順位によっては間違えたファイルをインクルードすることに。これを回避するためには優先順位を変更するかbaseclasses側のコードを変更して読み込まれるインクルードファイルを変更するか、となると思います。
最後に待っていた第三弾は最新のWindowsSDKの情報を追加した状態でライブラリを構築したはずなのですがライブラリの衝突か何かよくわかりませんが、必要な関数がライブラリにないためにビルドができない、という現象が。DirectShowが参照しているvsnwprintfを最新のWindowsSDKが定義していないらしく大変な状態となってしまいました。結局このままだとどうしようもないので今の段階では最新のWIndowsSDKを使わないように設定して構築したライブラリを使うことにしています。
ちなみに、最新のWindowsSDKでXAudio2のコードをコンパイルしようとすると「このライブラリを使うとWindows8以降でないと動作しないコードが作成されます。それ以前で動作させるためにはDirectXSDKのライブラリを使用してください」とのメッセージ(注:意訳です)が出ますのでこれも効いていたりします。
VS2010以降でライブラリを構築するときに依存性を設定しても自動でリンクされない
というわけでです。はい。ライブラリを構築していて依存するライブラリを自分の中に含めようとするときにVS2008までだとプロジェクトに依存性を持たせるだけで自動的にリンクされたのですが、VS2010以降だと依存性を持たせるだけでは自動的にリンクしてくれません。依存性を持たせると同時にプロジェクトの設定から[構成プロパティ]=>[ライブラリアン]=>[全般]=>[ライブラリ依存関係のリンク]を「はい」にする必要があります。ちなみに「依存性を持たせる」にもちょっとしたトリックがあり、単に依存性のチェックをしただけではリンクしてくれないこともあります。この場合はプロジェクトの設定から[共通プロパティ]=>[Frameworkと参照]で依存するプロジェクトを参照として追加する必要があります。
そしてsetのiteratorの仕様が変わったのね
正しくいうなら「あまりよくない仕様だったものが直された」というべきでしょうか。VS2010になってsetのiteratorの扱いがconst_iteratorと同じになっています。そのため、iterator経由であってもデータの書き換えができない、という状態になっています。確かにデータの書き換えができると二分木が保てなくなるのでその変更は正しいのですが、使い方によってはかなり影響がある変更です。たとえばこんな感じで使っているとちょっとやばい。
class CItem{ public: CItem(int no,string value) : _no(no), _value(value) { } ~CItem() { } const string &getvalue(void) const { return _value; } void setvalue(const string &value){ _value = value; } bool operator < (const CItem &item){ return _no < item._no; } private: int _no; string _value; }; set<CItem> setitem; set::iterator it; CItem val(1,"first"); setitem.insert(val); it = setitem.find(val); it->setvalue("second");
かなりいい加減なコードですが、こんな感じです。アイテムの集合体としてsetによる検索を有効にしておいて見つかったアイテムに対して(添え字を変えずに)アイテムの値を変える、というやつです。一応mapでもできますが、番号とデータが分離してしまうと扱いにくいというのもありsetでやっていたわけですが・・・。これができなくなるわけです。
回避する方法としては・・・。まずはsetではなくmapの使用を検討してみる、というところでしょう。ただ、添え字がデータがから分離するのでそれによって整合性が崩れないかどうか、を考える必要があります。そして緊急回避的な処置としてはconst_castをかませることで逃れる、ですね。たとえば例のコードで最後の部分を
const_cast<CItem &>(*it).setvalue("second");
や
const_cast<CItem *>(&(*it))->setvalue("second");
とすれば何とかなります。どちらにしてもまあ微妙ですか。
とりあえずは何とかなった
Win8への移行を開始してからかなり期間をかけましたが、なんとか安定して動作させる環境を構築できました。なお、どうでもよくない話としてAdvancedCodecsにやばいマルウェアが同梱されるようになった、という話を聞いたので今回はAdvancedCodecsに頼ることなく別のコーデックをいれて対応しています。さて、ここからどうしましょうかね・・・。まずはWindowModePatchの修正を続行するところからでしょうか。
ピンバック: 「マイクロソフトもグーグルも信頼できない」という嘆かわしい現実 – さあ,もう一度ゼロから始めよう