kkamegawa's weblog

Visual Studio,TFS,ALM,VSTS,DevOps関係のことについていろいろと書いていきます。Google Analyticsで解析を行っています

KB2517589では何故下位互換がなくなると説明しているか

An ADO application does not run on down-level operating systems after you recompile it on a computer that is running Windows 7 SP 1 or Windows Server 2008 R2 SP 1 or that has KB983246 installed
KB2517589のはてなブックマークに「Windows 7 SP1で生成したモジュールが下位OSで使用できない」とコメントつけられていました。びっくりしてよく読んでみて、はてぶでもコメント書いたのですが、100文字では足りないので、ここでもう少し解説します。
間違っているところがあればコメントください。
これほど苦労するならとっととADO.NETに行った方がいいと思います…たぶん。

前提

ADOおよび、ADO MD(Multi Dimention:ActiveX データオブジェクト)をCOM Interopを使って.NET Frameworkアプリケーションと相互運用することができます。しかし、負荷がかかっている状態ではたまにアプリケーションエラーが出るそうです。
具体的にはマルチスレッドアプリケーションのASP.NETでしばしば発生します。その現象について説明している話がKB840667となります。
You receive unexpected errors when using ADO and ADO MD in a .NET Framework application
回避方法としてはASP.NETの場合、@PageディレクティブでASPCompatをtrueに指定してほしいと書かれています。これにより、該当ページがSTAスレッドで動作するようになり、ADO/ADO MDに対してマルチスレッドアクセスを行わなくなるということだそうです。
ということもあるように、ADOおよび、ADO MDは.NET Frameworkでの動作を完全にテストしていない。特にサービスベースのアプリケーションでマルチスレッドで動くようなものはそもそも相互運用に問題があるのでできればやめてほしい。
.NET FrameworkではADO.NETがちゃんと用意されているんだから、ADO.NETに移行するまでのつなぎ程度で使ってくださいという大前提があります。

問題になるケース

そもそも相互運用で問題があることがわかっている混ぜるな危険テクノロジーだけど、それでもなお過去の資産を継続的に使おうとして、Windows 7 SP1でADOアプリケーションを以下の開発環境で再生成した場合に発生します。

これらの環境で再生したADOアプリケーションをWindows 7 SP1以前のOSで実行すると、以下のエラーメッセージが出るそうです。

  • REGDB_E_CLASSNOTREG (0x80040154)
  • E_POINTER (0x80004003)
  • E_NOINTERFACE (0x80004002)
  • Unable to cast COM object of type 'System.__ComObject' 〜(略)

なぜ起きるの?

ADOのインターフェース(正確にはIIDs)がWindows 7 SP1で変わっちゃったから。古い(Windows 7 RTMまでの)IIDsは_Deprecatedというsuffixがつくようになったそうです。

  • Windows 7 RTMまでのOSでは_Connection IID が 00000550-0000-0010-8000-00AA006D2EA4だった
  • Windows 7 SP1以降は _Connection IIDが 00001550-0000-0010-8000-00AA006D2EA4で、以前の互換IIDは_Connection_Deprecatedという名前になり、IIDは00000550-0000-0010-8000-00AA006D2EA4のまま。

よって、アーリーバインディング(つまり起動時に呼び出してしまうやつコンパイル時結合)しているようなアプリケーションではIIDが存在しないため、エラーになっちゃうと。
なんでこんな変更したかといえば、ADOのAPIのうち、ADO 2.7以降のバージョンではx64 Windowsでは(マクロ定義によって)64bitのデータ型が引数になってしまうものがあった(LONGLONGなど)んだけど、アプリケーション側の引数をLONG型変数でとっているものがまだあったので、型変換時に問題が発生してしまい、アプリケーションに問題が出たからだそうです(一言でいえば、x64プラットフォームで動くアプリケーションについて理解していなかったということかな)。

回避策

以下の四つのうちいずれか。どれを使っても大変…かも。

(突っ込みいただいて、アーリーバインディングの記述を変更)