Win10: OS のバージョン

Windows 10 RTM が目前に迫ってきました。
常に付きまとうバージョン問題。ここらで、一度、Windows 10 のバージョン値を確認しておこうと思います。
バージョン番号を取得する際、お約束の GetVersion() と GetVersionEx() になりますが、実は
Windows 8.1 以降では、GetVersion() および GetVersionEx() は推奨されない API になっていて
versionhelpers.h で定義されている Version Helper 関数を使うように推奨されています。
ただ、Version Helper 関数だと、具体的なメジャーバージョン、マイナーバージョンの値を取ることができず
Windows 10 以降とか、Windows 7 以降とか、そういう判定しかできない。
おそらく、Microsoft としては、もうメジャーバージョンとかマイナーバージョンで判断するのは辞めれって
メッセージなのでしょう。
実際、Windows 10 以降、OS メジャーアップグーレードの概念を変えるとアナウンスされていますし。

MSDN によると、OS のバージョン番号を取るにはシステム DLL に対して、GetFileVersionInfo() で見なさいと
言っているので、ちと面倒だ。DllGetVersion() という API ものがあるのですが、これは Shell 系の API なので
サービスからは使えないので、やはり、GetFileVersionInfo() を使うことになるのか。

試したところ、Windows 10 でも、まだ、GetVersionEx() が使えるようなので、こっちを使ってバージョン値を
調べてみますが、いつ何時消えるかもしれないので、製品開発では使わない方がいいと思います。

OSVERSIONINFOEX osvi;
BOOL bOsVersionInfoEx;

// OSVERSIONINFOEX構造体を用いて、GetVersionEx()を呼び出してみる
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *)&osvi);
if (!bOsVersionInfoEx)
{
    osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
    if (!GetVersionEx((OSVERSIONINFO *)&osvi))
    {
        return GetLastError();
    }
}

Windows 8.1 / Windows Server 2012 R2 では、アプリケーションマニフェストの SupportedOS で
明示的な 8.1 宣言をしないと、GetVersionEx() は Windows 8 の 6.2 を返すという互換性処理がありました。
Windows 10 はどうなのかな…というわけでやってみます。
なお、Windows 10 の supportedOS の guid は MSDN ですでに公開されています。

<!– Windows 10 –>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!– Windows 8.1 –>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!– Windows 8 –>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!– Windows 7 –>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!– Windows Vista –>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>

まずは、マニフェストで supportedOS を記述しなかった場合は…

マニフェストなし

内部バージョンは 6.2 で返します。 ということは、Windows 8 ってことか。
次にマニフェストで supportedOS に Windows 8 を記述してみると…

Win8マニフェスト

まぁ、当然ですが、6.2 を返すわけです。
では、supportedOS に Windows 8.1 を記述してみると…

Win81マニフェスト

あー、6.3 ですか。そうですか。
では、最後に supportedOS に Windows 10 を記述してみると…

Win10マニフェスト

おー、きたきた!! バージョン情報が大きく上がった!!
では、まとめます。

OS 名称 バージョン値#1 バージョン値#2 バージョン値#3
Windows 10 10.0
supportedOS に Win10
6.3
supportedOS に Win8.1
6.2
Windows 8.1 / Windows Server 2012 R2 6.3
supportedOS に Win8.1
6.2 N/A
Windows 8 / Windows Server 2012 6.2 
N/A N/A
Windows 7 / Windows Server 2008 R2 6.1 N/A N/A
Windows Vista / Windows Server 2008 6.0 N/A N/A

MSDN に記載がある通りの結果になりました。
Windows 8 まではマニフェストの supportedOS への記述では OS モードだけの差異でしたが、Windows 8.1 以降は
OS バージョンと ビルド番号も変更しています。
しかし、Windows 10 で内部バージョンが一気に上がりましたねぇ。

Win8 & 2012: アプリケーション マニフェストの SupportedOS

Windows 7 & Windows Server 2008 R2 で追加になった Compatibility セクションの SupportedOS ですが
Windows 8 & Windows Server 2012 用に GUID が追加されています。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!–Windows 8 Mode–>
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
      <!–Windows 7 Mode–>
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /
      <!–Windows Vista Mode–>
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
    </application>
  </compatibility>
</assembly>

  • Compatibility セクションは Windows 7 からサポートされる
  • Compatibility セクションがないアプリケーションは Windows Vista モードで動作する
  • Windows 7 以降の OS では、Compatibility セクション内の最も新しい OS レベルで実行モードを決定する

OS 実行モードの違いは次のようになります。

項目 Windows 8 Windows 7 Windows Vista
RPC の既定スレッドプール NT スレッドプール NT スレッドプール プライベートスレッドプール
DirectDraw の Lock API によるプライマリデスクトップビデオバッファーのロック 不可 不可
DirectDraw のクリッピングウィンドウなしでプライマリデスクトップビデオバッファーへのビットブロック転送 不可 不可
GetOverlappedResult API の複数回呼出しでの競合 競合は解決される 競合は解決される 競合は解決されない
ハイ コントラスト モードでの Shell Theme のステータス テーマ設定状況が返される テーマ設定できないことが通知される テーマ設定できないことが通知される
IPersistFile ハンドラー呼出しへの相対パスの使用 呼び出しに失敗する 続行する 続行する
プログラム互換性アシスタント (PCA) PCA による軽減策は適応されない PCA による互換性問題が追跡される PCA による互換性問題が追跡される

モードの確認方法も Windows 7 のときと同じで、リソースモニターを使うことで確認することができます。

リソースモニター

Windows 7 では、Windows Vista モードの実行モジュールが目立ちましたが、
Windows 8 では、多くの実行モジュールが Windows 8 モードや Windows 7 モードになりました。

ちなみに、Windows 8 の内部バージョンは 6.2 になります。

Win7: インストーラーの PCA 対策

Windows Vista で導入された機能の 1 つに「インストーラーの検出機能」があります。

  • 特定パターンに合致するプログラムをインストーラーとして検出する
  • ファイル名やリソースに特定の文字(setup、update、install など)を含む場合、インストーラーとみなす

インストーラーとして検出された場合には、OS が自動的に権限昇格を提供して、管理者権限で実行されるように
なります。そして、インストーラーがエラーとなった場合には、PCA(Program Compatibility Assistant)が
介入して、互換アシスタントダイアログ を表示します。

ちなみに、互換アシスタントダイアログ に表示される ”プログラム” や ”発行元” は、バージョンリソースから持ってきます。
バージョンリソースがないと ”不明” と表示されてしまいます。

インストーラープロセスに対して、PCA が動作する条件ですが、

  • UAC で権限昇格している
  • プログラムの追加と削除への登録がない
  • 32bit プログラム
  • マニフェストにより実行権限を指定していない

というものがあります。(なお、この機能はクライアント OS のみになります)
インストーラーの動作確認や開発を行う場合には、必ず、頭に入れておかなければなりません。

ところが、Windows 7 からは、上記の条件に

  • Windows 7 ネイティブモードではない(マニフェストの Compatibility セクションで Windows 7 の指定がない)

というものが加わります。
Compatibility セクションについては、以前の記事「アプリケーション マニフェスト」を参照してください。

やっかいなのが、インストーラーを途中でキャンセルしても PCA が働いてしまうということです。

では、互換アシスタントダイアログ を表示されないようにする対策方法ですが、もうお気づきの通りで、

インストーラーをWindows 7 ネイティブモードで動くようにする

しかありません。
つまり、インストーラーの埋め込みマニフェストに Compatibility セクションを追加するわけです。
(必ず、埋め込みマニフェストにする必要があります)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
・・省略・・
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
<!--Windows 7--> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /> </application> </compatibility> </assembly>

こんな感じで、プログラムの実行レベルと一緒に Compatibility セクションを記述したマニフェストを
インストーラーに埋め込むことになります。

なお、supportedOS ですが、上記に Windows Vista である

<!–Windows Vista–>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />

を記述して supportedOS が 2 つになった場合ですが、Windows 7 では、Windows 7 ネイティブを優先します。
そのため、2 つ書いても意味がないです。
Windows Vista では、そもそも Compatibility セクションがサポートされていませんので無視されるだけです。
(もちろん、Vista より前の OS でも同様です)

PCA の 互換アシスタントダイアログ が表示されると見た目もかっこ悪く、何よりも、ユーザーは

”これ、本当に Windows 7 対応しているのだろうか?”

と疑問を持ちますからね。
Windows 7 対応するインストーラーは、マニフェストに Compatibility セクションを追加するようにしましょう。

参考情報

追記(2009.09.15)
Tech Fielders コラムに参考情報が記載されたと MS エバの方より連絡を受けました(ありがとー)。

チェックしておくといいと思います。

インストーラー開発に InstallShield を使用している場合には、

を見ておいてください。

Win7&2008R2: アプリケーション マニフェスト

マニフェストの基本的な内容は、Vista / 2008 と同様です。

UAC マニフェスト(exe の実行権限を定義する)

  • {ファイル名}.exe.manifest 内に定義(XML ファイル)
<requestedExecutionLevel level="asInvoker" uiAccess="false" />

 level の値  意味
 asInvoker  親プロセスと同じ権限で動作
 highestAvailble  ユーザーが取得可能な権限で動作
 requireAdministrator  管理者権限で動作
 uiAccess の値  意味
 false  通常のアプリケーション
 true  UIPI の制限を回避
  • 添付方法
    exe に埋め込む(ビルド or MT.EXE)…埋め込みマニフェスト
    exe と同じフォルダに配置する…外部マニフェスト

注意点は 2 つあります。

  1. マニフェストの優先順位
    Vista 以降は 埋め込みマニフェスト を優先する
    Vista より前は 外部マニフェスト を優先する
  2. 外部マニフェストが有効にならない?
    OS が一度動作させた情報をキャッシュしてしまっているために、発生する。
    (パスと実行モジュールのタイムスタンプを見ている)
    こんなときには、タイムスタンプを変える(リビルド)とか、パスを変えるようにします。

そして、Windows 7 からは新しい項目「compatibility セクション」が追加されました。

compatibility セクション(互換性情報)(Windows 7 の新機能)

Compatibility セクションですが、Windows 7 & 2008 R2 からのサポートになります。
情報は以下の MSDN にありますので、目を通しておいた方がよいと思います。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!--Windows 7-->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
      <!--Windows Vista-->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
    </application>
  </compatibility>
</assembly>

Compatibility セクションのないマニフェストをもつアプリケーションは Windows Vista モードで動作することになります。
ここは何気に重要なことを意味していて、これが

Windows Vista 用のアプリケーションは Windows 7 上でも互換性がある

という種明かしなんです。
こうなると、Windows 7 ネイティブモードって何?ってことになりますよね。

  • RPC Default Thread Pool
    7: RPC が NT スレッドプールを使う
    Vista: RPC がプライベートスレッドプールを使う
  • DirectDraw Lock
    7: DDARW のビデオバッファをロックできない
    Vista: DDARW のビデオバッファをロックできる
  • DirectDraw Bit Block Transfer (Blt) to Primary without Clipping Window
    7: クリッピングウィンドウ無しで Blt エリアに描画ができる
    Vista: Desktop Window Manager に従い、描画する
  • GetOverlappedResult API
    7: マルチスレッドで使用した場合の競合を解決する
    Vista: マルチスレッドで使用した場合、タイミングによっては期待した結果が得られない
  • Program Compatibility Assistant (PCA)
    7: PCA を使わない
    Vista: PCA を使う

この Compatibility セクションにある supportedOS ですが、OS 種別を GUID で記述する必要があるようで
注意が必要です。間違えないようにしないといけませんね。
Visual Studio 2008 で使うには、こんな使い方になると思います。

  1. Compatibility セクションを記述したマニフェストを用意して、プロジェクトの Resource Files に追加する
  2. プロジェクトの構成プロパティで、マニフェスト ツール の”追加のマニフェスト ファイル”で作成したマニフェストを追加
  3. ビルドする

VS のエディタは、XML ファイルを作成するときにも便利なので、使わないとね。
そして、最後に Compatibility セクション の確認方法を紹介します。
やはり、作成したアプリケーションがどっちのモードで動いているのか知りたいですよね。
これには、Windows 7 のリソースモニターを使います。

こんな感じで、オペレーティング システムのコンテキストという項目に表示されます。
ここを見ると、Windows 7 モードなのか、Windows Vista モードなのかがわかります。

よーく見ると、OS コンポーネントでも Windows Vista モードで動いているものが目立ちますね。