基本的な本能
Visual Studio 2010 における複数バージョン対応の Visual Basic アプリケーション
Spotty Bowles
Visual Studio 2008 より前は、Microsoft .NET Framework の異なるバージョンに対応するアプリケーションを作成するには、異なるバージョンの Visual Studio 開発環境をインストールする必要がありました。Visual Studio の各インストールでは、開発者エクスペリエンスがそれぞれ異なり、各インストールにかなりのディスク容量が使用されていました。さらに、プロジェクトのファイル形式も、Visual Studio の各バージョン間で異なっていました。そのため、.NET Framework の異なるバージョンに対応するプロジェクトで使用するコンポーネントを開発する際は、複数バージョンのプロジェクトやソリューションを作成することになりました。
Visual Studio 2008 からは、複数バージョンへの対応が 1 つの IDE 内で完全にサポートされるようになり、開発者は、1 つの Visual Studio をインストールするだけで、さまざまなバージョンの .NET Framework (2.0、3.0、および 3.5) に対応するアプリケーションを作成できます。その結果、1 つの一貫した開発者エクスペリエンスが実現し、ディスク容量の要件が減少します。
Visual Studio 2008 の複数バージョン対応が機能していたのは、対応可能な各フレームワークの基盤に同じ CLR 2.0 が使用されていたためです。また、フレームワークの各バージョンが .NET Framework 2.0 を基盤に構築され、追加機能はアセンブリの参照を利用して提供されていました。最終的には、すべてのバージョンで、.NET Framework 3.5 のコマンド ライン Visual Basic コンパイラ (vbc.exe) が使用されていました。
今月のコラムでは、.NET Framework 3.5 と .NET Framework 4 の個別インストールの一部としてインストールされるそれぞれのコンパイラについて説明します。.NET Framework 3.5 のコンパイラは Visual Studio 2008 と Visual Basic 9 に付属するバージョンで、.NET Framework 4 のコンパイラは Visual Studio 2010 と Visual Basic 10 に付属するバージョンです。
では、現在 Visual Studio で機能している複数バージョン対応のしくみと、複数バージョン対応のプロジェクトに取り掛かる方法について説明しましょう。
Visual Studio の複数バージョン対応
Visual Studio 2008 での対応フレームワークへの変更は、プロジェクトのプロパティにあるドロップダウン リストで対象を選択するだけの簡単な処理です (図 1 参照)。特定の参照の追加または削除により、各フレームワークのバージョンが要求され、フレームワークが簡単に変更されます。
図 1 Visual Studio 2008 での対応フレームワークへの変更
コマンド ライン コンパイルの場合は、使用する参照アセンブリを変更するだけです。
ただし、Visual Studio 2010 ではいくつか大きな変更が加えられています。新しい .NET Framework 4 は、新しいバージョンの CLR を採用しています。つまり、Visual Studio 2008 で使用していたアプローチは Visual Studio 2010 では使用されなくなります。そのため、Visual Studio 2010 では、たとえ .NET Framework の以前バージョンに対応していても、すべての複数バージョン対応に .NET Framework 4 コンパイラが使用されます。これにより、下位バージョンに対応する場合でも多数の新しい言語機能を使用できるだけでなく、開発エクスペリエンスが大きく簡素化されます。
しかし、下位バージョンに対応している場合に Visual Studio 2010 の機能を使用できるようにしたときに、ソース ファイルを以前のバージョンの Visual Studio で使用すると、デザイン時の互換性がなくなるという問題が生じることがあります。さまざまなバージョンの Visual Studio を使用し、.NET Framework の異なるバージョンを対象にしてビルドされた複数のプロジェクトでソース コードを共有している場合は、これが問題になることがあります。
プロジェクトのすべてのデザイン作業を Visual Studio 2010 内で行えば、優れたエクスペリエンスが実現されます。Visual Studio 2010 と .NET Framework 3.5 SP1 のみを使用して、.NET Framework 2.0 以降に対応するアセンブリを生成することができます。
デザイン時の互換性
デザイン時の互換性に関するヒントとなる例を見てみましょう。図 2 のコードでは、暗黙の行継続と自動実装プロパティの両方の機能が使用されています。これらは、Visual Studio 2010 で導入された機能です。Visual Studio 2010 を使用してコンパイルすれば、このコードを、2.0 以降のすべてのフレームワークに対応するようにコンパイルでき、生成されるアセンブリにも実行時の互換性があります。
図 2 下位レベル対応バージョンで機能する新しい言語機能の使用
しかし、これと同じソース コードファイルを、バージョン 3.5 または 2.0 のコンパイラでコンパイルしようとすると、図 3 に示すエラーが生成されます。
図 3 Visual Studio 2008 とのデザイン時の互換性がない Visual Studio 2010 のソース コード
このエラーは、以前のバージョンのコンパイラがこれらの機能をまったく認識せず、これを無効なコードと見なすために発生します。したがって、ソース ファイルにはデザイン時の互換性がありません。ここでデザイン時の互換性を維持するには、3.5 のコンパイラで使用できる機能のみを使用しなければなりません。
デザイン時の互換性は、Web プロジェクトにも同様の影響を与えます。多くの Web プロジェクトでは、サーバー上でコンパイルが行われます。もちろん、サーバーでは、サーバーにインストールされている対象フレームワークのコンパイラを使用してページがコンパイルされます。そのため、3.5 のコンパイラを対象にしている Visual Basic で記述された Web ページであれば、Visual Basic コンパイラ (vbc.exe) のバージョン 3.5 を使用して、サーバー上でコンパイルされます。3.5 コンパイラは、新しい Visual Studio 2010 の言語機能をまったく認識しないため、これらの機能を使用すると失敗します。
バージョン 4 のコンパイラを使用して、Visual Studio 2010 で開発されるコードの場合、Web ページをサーバーに配置するときに予期しないエラーが発生するのを防ぐために、コンパイル時にこの要件を特定する方法が必要です。これは、/langversion スイッチを使用して行います。このスイッチを Web プロジェクトの開発時に指定し、以前のフレームワークのコンパイラではコンパイルできない新しい言語構文機能を使用すると、エラーが生成されます。ASP.NET プロジェクトをビルドするときは、このスイッチが内部で使用され、コードに新しい Visual Studio 2010 機能が含まれていて、以前のバージョンのフレームワークを対象にしている場合はエラーが生成されます。
他のプロジェクトの種類では、/langversion スイッチが既定で使用されることはありませんが、ソース コードに以前のバージョンの Visual Studio とのデザイン時の互換性があることを確認する場合に、役に立つ可能性があります。
Visual Studio 2010 IDE での複数バージョン対応
Visual Studio 2010 IDE での複数バージョン対応のユーザー エクスペリエンスは、Visual Studio 2008 とほぼ同じです。複数バージョン対応をプロジェクトのプロパティ内から引き続き制御できますが、既定のインストールでは、以前の対象フレームワークが表示されないことがあります。インストール サイズを減らすために、Visual Studio チームは、Visual Studio 2010 の既定のインストールに 3.5 フレームワークを同梱しないことにしました。この変更により、[対象のフレームワーク] ボックスや [新しいプロジェクト] ダイアログ ボックスに、これらのフレームワークのオプションが表示されません。
これらのフレームワークを追加するには、.NET Framework 3.5 SP1 をインストールする必要があります。これは IDE から実行できます。[新しいプロジェクト] ダイアログ ボックスの上部に、対象フレームワークを選択するドロップダウン メニューが表示されます。.NET Framework 4 しかインストールされていなければ、多くのフレームワークをダウンロードするためのリンクがメニューに表示されます。ただし、他のフレームワークをインストールしている場合は、ドロップダウン メニューに .NET Framework 3.5 SP1 のみが表示されます。それは、Visual Studio が .NET Framework 3.5 SP1 のインストールのみを認識するためです。
もう 1 つの変更は、Client Profile に関連します。Client Profile は .NET Framework 3.5 SP1 で導入されました。これにより、アプリケーションは簡易バージョンのフレームワークを使用できます。サーバー側のフレームワーク (ASP.NET など) を含める必要がないことから、配置のパフォーマンスが向上します。これらのプロファイルは、3.5 と 4 の両方のフレームワークを対象とする場合に使用できます。
そのため、さまざまなプロジェクトの種類の既定のプロファイルが変更されています。クライアント プロジェクトの種類 (Window アプリケーション、コンソール アプリケーション、Office アプリケーション、および Windows Presentation Foundation (WPF) アプリケーション) では、既定で Client Profile が使用されます。ただし、Web アプリケーションでは、ライブラリへの参照 (System.Web など) が Client Profile を使用して配置されないため、既定のプロファイルは "full (すべて)" になります。
クラス ライブラリでも、既定で Full Profile が使用されますが、Client Profile を使用して配置される参照のみに依存している場合は、Client Profile に簡単に戻すことができます。クラス ライブラリが Full Profile に設定されていて、このクラス ライブラリが Client Profile を指定したプロジェクトで使用されても、このライブラリがクライアント フレームワークのアセンブリに含まれない参照に依存しない限り、機能します。
既定では、クラス ライブラリ プロジェクトの種類に追加される参照に、Full Profile が必要になる参照はありません。ただし、ライブラリはアプリケーションと共に配置されるため、アプリケーションの配置プロファイルは、完全なアプリケーション機能を確保するうえで重要な設定です。ライブラリがクライアント スコープ外にある参照に依存する場合、それを使用するライブラリとアプリケーションの両方で Full Profile を使用する必要があります。
コマンド ライン コンパイラを使用する複数バージョン対応
バージョン 4 のコンパイラには、いくつかのコマンドライン スイッチがありますが、残念ながら、これらのスイッチでは対象フレームワークが制御されません。したがって、各スイッチとその機能について少し理解しておくことが重要です。
.NET Framework 4 がインストールされている場合は、vbc.exe を使用してアプリケーションをビルドすることで、ビルド コンピューターに Visual Studio をインストールすることなく、以前のバージョンのフレームワークに対応できます。大規模開発環境では、多くの場合、コマンド ライン コンパイラを直接呼び出すビルド スクリプトが使用されます。コマンド ラインから以前のバージョンを対象に指定する場合は、対象とする以前のフレームワーク バージョンを指定してファイルをインストールする必要があります。したがって、.NET Framework 3.5 SP1 と .NET Framework 4 の両方をコンピューターにインストールしておくのが最善の方法です。
これを念頭に置いて、複数バージョン対応に関係する可能性があるいくつかのスイッチを図 4 に詳しく示します。
図 4 複数バージョン対応を制御するコマンド ライン ビルドのスイッチ
スイッチ | 説明 |
langversion | 特定の言語バージョンが対応しない機能を使用しているソース コードにエラーが生成されます (9.0 は .NET Framework 3.5 までを対象とする場合に関連し、10 は .NET Framework 4 を対象とする場合に関連します)。これによって、対象フレームワークや使用される CLR が実際に決定されるわけではありませんが、Web プロジェクトの場合は、下位フレームワークを対象とするシナリオで使用される Visual Studio 2010 機能を特定できます。 |
vbruntime | .NET Framework 4 向けにはさまざまなバージョンの Microsoft.VisualBasic がありますが、Microsoft.VisualBasic.dll の 2.0 バージョンの指定を試みるだけでは機能せず、結果的には、バージョン 4 の NetFX に依存するアセンブリになります。 |
nostdlib | アセンブリに system.dll への標準参照が追加されないようにします。このオプションを、2.0 のフレームワーク バージョンの system.dll への参照と同時に使用できますが、結果的にはバージョン 4 のアセンブリのままになります。 |
sdkpath | vbruntime スイッチで、MSCorLib.dll と Microsoft.VisualBasic.dll のどちらを使用するかを指定しない場合に、これらの dll の使用バージョンを指定する重要なオプションです。ただし、指定された dll は、通常参照の一覧に表示される明示的な参照になるわけではありません。代わりに、コンパイラがその標準参照にこれを含めます。このスイッチを追加することは、バージョン 4 ではなく、バージョン 2.0 の MSCorLib を対象とする場合の、複数バージョン対応のソリューションの一部です。 |
noconfig | コンパイラが、vbc.rsp ファイルに含まれている既定の参照、インポート、およびスイッチを追加しないようにします。このスイッチを指定しないとこれらが使用されます。 |
この表では、各スイッチについて簡単に説明していますが、下位フレームワークを対象とするコンパイルを実際に行うには、これらのスイッチを組み合わせて使用する必要があります。複数バージョン対応のスイッチは 1 つではありません。バージョン 3.5 を対象とする場合、最も重要なスイッチは sdkpath で、MSCorlib の 2.0 バージョンを指定するのに使用します。次に、プロジェクトの参照が、System.dll、System.core.dll、および他の下位対象フレームワーク アセンブリの適切なバージョンを指していることを確認します (これらは、%programfiles%\Reference Assemblies\Microsoft\Framework\v3.5 にあります)。
noconfig スイッチを指定して、バージョン 4 の vbc.rsp にある既定のスイッチを使用しないようにする必要があります。このファイルには、コンパイルの既定の設定が含まれています。この重要なスイッチを追加しないと、コンパイラによって、既定のバージョン 4 の参照やインポートなどが追加されます。
複数バージョン対応のコマンド ライン コンパイルを例を挙げて最もわかりやすく示します。ここでは、簡単なソース ファイル (test.vb) を、.NET Framework 3.5 を対象としてコンパイルするとします。
vbc.exe /noconfig /sdkpath:D:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 /r:"D:\Program Files\ReferenceAssemblies\Microsoft\Framework\v3.5\System.Core.dll" d:\school\test.vb /out:\school\test.exe
3.5 のアセンブリをコンパイルするスイッチを把握したところで、次に 2.0 を対象とするためには、3.5 のフレームワークに必要な一部の参照 (system.core.dll など) を削除するだけです。sdkpath スイッチと noconfig スイッチはそのまま使用します。
vbc.exe /noconfig /sdkpath:D:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 d:\school\test.vb /out:\school\test.exe
コンパイル済みのバイナリがある場合、MSIL 逆アセンブラー (Ildasm.exe) や .NET Reflector などのツールを使用して、そのバイナリで使用されている参照のバージョンを確認できます。これにより、目的の対象フレームワークで、実行可能ファイルが実行されるかどうかを判断できます。
Client Profile と対象フレームワークが混在する場合のソリューション
少し前に、ほとんどのプロジェクトの種類で、Client Profile が既定で使用されることに触れました。このようなアプリケーションを配置すると、インストールされるフレームワークのフットプリントが少なくなります。このような配置の一環として、コマンド ライン コンパイラも配置されます。配置されるのは、完全版フレームワークと同じバージョンのコンパイラですが、Client Profile 環境で単純なアプリケーションをコンパイルしようとすると、問題が発生する可能性があります。
クライアント フレームワークには、既定の参照を含む vbc.rsp が付属していないため、クライアント フレームワークがインストールされているコンピューターで次のコマンドを使用すると失敗することになります。
vbc.exe test.vb /out:test.exe
vbc.rsp ファイルに通常含まれるすべての参照と imports ステートメントを指定するか、このファイルを独自に作成する必要があります。
クライアント フレームワークがインストールされている環境で Visual Basic アプリケーションをコンパイルするのに必要最低限のスイッチは次のとおりです。
/r:System.dll/imports:System/imports:Microsoft.VisualBasic
これらのスイッチを含めることで、基本的な Visual Basic アプリケーションをコンパイルできます。ただし、アプリケーションは、完全版のフレームワークがインストールされているコンピューターでコンパイルすべきです。
.NET Framework 3.5 でビルドされたクラス ライブラリを、.NET framework 4 を対象とするクライアント アプリケーションで使用する場合など、対象とするフレームワークが混在するソリューションもサポートされますが、いくつか注意事項があります。Visual Studio 2010 IDE 内では、プロジェクト参照を使用していて、このプロジェクト参照の操作性に慣れている場合、プロジェクト参照の対象フレームワークで同じバージョンの MSCorlib を使用すれば、この操作性をそのまま利用できます。図 5 に、MSCorlib の各バージョンと、サポート対象のフレームワーク バージョンを示します。
図 5 MSCorlib とフレームワークのバージョン互換性
MSCorlib のバージョン | サポート対象のフレームワーク | プロファイル |
2.0 | 2.0、3.0、3.5 | Client Profile および Full Profile |
4.0 | 4 | Client Profile および Full Profile |
したがって、.NET Framework 3.5 アプリケーションで MSCorlib 2.0 を対象とするクラス ライブラリを使用していれば、プロジェクト参照をそのまま使用できます。同様に、.NET Framework 4 の Client Profile Windows アプリケーションから参照される .NET Framework 4 の Full Profile クラス ライブラリには、そのライブラリへのプロジェクト参照を含めることができます。
ただし、異なるバージョンの MSCorlib を使用するプロジェクト間の参照を使用すると、そのプロジェクト参照はファイル参照に変換されます。つまり、エラーを解決するときはソリューションを手動でリビルドする必要があります。C# と Visual Basic の両方で記述された複数参照のプロジェクトを含むソリューションで作業した経験があれば、この操作性にはなじみがあるでしょう。プロジェクト間での名前の変更や自動バックグラウンド コンパイルなど、プロジェクト参照で使用できるいくつか便利な機能が失われます。
IDE でコンパイルすれば、内部で何が行われているかはある程度隠されますが、誰もが IDE からビルドするわけではありません。対象フレームワークが変更されると、両方で共通バージョンの MSCorlib を含むフレームワークを使用するように、ファイル参照が自動的にプロジェクト参照に戻されます。したがって、これは本当のファイル参照ではありません。
バージョン 4 のアプリケーション内で、下位バージョン対応 (3.5) のクラス ライブラリを使用するとどうなるでしょう。クラス ライブラリは、実際には .NET Framework 4 を対象に実行されます。このシナリオが実行時にほとんど問題なく機能するように、かなりの量のテストが行われています。ただし、3.5 フレームワークのアプリケーションから 4.0 フレームワークのクラス ライブラリの使用を試みることはサポート対象外で、Visual Studio か MSBuild のいずれかを使用してビルドすると、コンパイル時エラーが発生することになります。3.5 フレームワーク対応のアプリケーションで 4.0 フレームワークのクラス ライブラリを使用するには、このクラス ライブラリの対象を下位の .NET Framework 3.5 にする必要があります。
ただし、下位フレームワークを対象とするシナリオで Visual Studio 2010 の言語機能を使用できれば、クラス ライブラリの対象を .NET Framework 3.5 にすることは大きな問題ではありません。図 6 に、下位フレームワークを対象とするプロジェクトで機能すると想定されている新機能をまとめます。
図 6 下位フレームワークを対象とするシナリオでの新しい Visual Studio 機能
言語機能 | 下位フレームワーク対象のシナリオで機能かどうか |
コレクション初期化子 | ○ |
配列初期化子 | ○ |
自動実装プロパティ | ○ |
暗黙の行継続 | ○ |
ステートメント型のラムダ | ○ |
No-PIA | × |
動的な相互運用 | × |
共変性と反変性 | 部分的に○ |
PIA と相互運用
.NET Framework を使用して Microsoft Office オブジェクト モデルに対してプログラミングする場合は、プライマリ相互運用機能アセンブリ (PIA) を使用する必要があり、これをユーザーのコンピューターに配置しなければなりません。多くの場合、これらのアセンブリは非常に大きく、配置が厄介になることがあります。
新しい型の埋め込み機能を使用すると、ユーザーのコンピューターで PIA を必要とすることなく、こうしたアプリケーションを配置できます。これは、COM ライブラリへの相互運用機能呼び出しを直接実行する、埋め込み相互運用型を生成することで実行されます。このような型にはコンパイラによって注釈が付けられるため、CLR は埋め込み相互運用型のすべてのインスタンスを等価なものとして扱います。コンパイラは、PIA のすべての型をアセンブリにコピーするのではなく、実際に使用されている型だけをコピーします。詳細については、MSDN ライブラリの「型の等価性と埋め込まれた相互運用機能型」(msdn.microsoft.com/ja-jp/library/dd997297) を参照してください。
この機能は、.NET Framework 4 より前の下位フレームワークを対象とするシナリオではサポートされません。Visual Studio IDE を使用していると、このような現象はなかなか明らかになりません。Visual Studio IDE では、下位フレームワークを対象とするシナリオで埋め込み相互運用プロパティの参照を true に設定すると、通常の参照を使用するようにしているためです。IDE 機能のユーザー エクスペリエンスでは、アセンブリのビルドが続行されますが、標準の参照の動作に戻されることになるため、PIA の配置が必要になります。
コマンド ラインから、/reference スイッチを使用して参照を追加するのが一般的です。埋め込みの場合は、代わりに /link スイッチを使用します。下位フレームワークを対象とするシナリオで /link スイッチを使用しようとすると、コンパイル エラーが発生します。
Word の相互運用アセンブリからの、コマンド ラインの埋め込み型の例を次に示します。
D:\Windows\Microsoft.NET\Framework\v4.0.30128\Vbc.exe /imports:Microsoft.VisualBasic,System /link:"D:\Program Files\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.Word.dll" /reference:"D:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\System.Core.dll","D:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\System.dll" /out:ConsoleApplication16.exe /target:exe Module1.vb
既定では、Visual Studio 2010 のプロジェクトに追加された COM 参照では、Embed Interop プロパティが true に設定されるため、この動作は重要です。したがって、対象フレームワークが変化しても新たなエラーが発生することはありませんが、可能な場合は、埋め込み相互運用型のメリットがもたらされます。
下位フレームワークを対象とするシナリオでサポートされない、Visual Studio 2010 のもう 1 つの新機能は、動的相互運用です。これは、Visual Studio 2010 より前には、動的言語ランタイム (DLR) が存在していなかったためです。
その他の問題点
共変性や反変性は、ユーザー定義インターフェイスで使用するためにサポートされています。ただし、下位レベルのフレームワークを対象とする基本クラス ライブラリ (BCL) インターフェイスは変更されないため、この機能をこのような基本クラスと併用することはサポートされていません。共変性と反変性の詳細については、2010 年 3 月号の MSDN Magazine の「基本的な本能」コラム (msdn.microsoft.com/magazine/ee336029) を参照してください。
以前のバージョンの Visual Studio で作成されたプロジェクトやソリューションを Visual Studio 2010 で開くと、標準のアップグレード ダイアログ ボックスが表示されます。Visual Studio では、Visual Studio 2010 で機能するように、プロジェクト ファイルやソリューション ファイルに必要な変更が加えられます。ただし、ファイルのアップグレードには 2 種類の操作が関与し、これが複数バージョン対応に影響を及ぼす可能性があります。
.NET Framework 3.5 SP1 がインストールされている場合は、アップグレード ダイアログ ボックスを使用して、プロジェクト ファイルやソリューション ファイルを Visual Studio 2010 にアップグレードできますが、プロジェクトで指定されている対象フレームワークは変更されないままになります。そのため、.NET Framework 3.5 を対象とするアプリケーションをアップグレードしても、アップグレード後も対象は依然として 3.5 フレームワークです。
.NET Framework 3.5 SP1 がインストールされていない場合は、2.0 バージョンの MSCorlib と参照アセンブリが必要になるため、複数バージョン対応を正しくビルドできません。ダイアログ ボックスでは、対象をバージョン 4 のフレームワークに変更するオプションや、プロジェクトをアップグレードしないオプションもあります。この場合の最善策は、アップグレードを中止して、.NET Framework 3.5 SP1 をインストールしてから、プロセスを実行してプロジェクトを再度アップグレードすることです。
Visual Studio 2010 における Visual Basic の複数バージョン対応の実装の詳細について少し詳しく理解すれば、アセンブリを生成するコードを記述して、IDE やコマンド ラインを使用して以前のバージョンのフレームワークに配置でき、さらに、いくつか新しい Visual Studio 2010 機能も活用することができます。複数バージョン対応にはいくつか注意事項がありますが、.NET Framework 4 を使用するようにすぐにはアップグレードできないアプリケーションを開発および配置する機能が得られます。
Adrian Spotty Bowles は、すべてのバージョンの Visual Basic による開発を経て、現在はワシントン州レドモンドで、Visual Basic 製品チームのソフトウェア設計エンジニア/テスターとして、Visual Basic コンパイラを担当しています。彼は、今もなお Visual Basic に対して情熱を持っていて、MSDN の Visual Basic フォーラムでよく質問に回答しています。Bowles の連絡先は Abowles@microsoft.com (英語のみ) です。
この記事のレビューに協力してくれた技術スタッフの Kevin Halverson と Beth Massi に心より感謝いたします。