あとはよしなに -3ページ目

ClickOnceプログラミング 其の弐

ClickOnceプログラミング 其の弐 「差分ダウンロード機能の仕組みと罠パターン」


初回は「差分ダウンロード機能」について掘り下げてみます。


では、ちょっとだけ前置きします。


差分ダウンロード機能とは、「変更されてないファイルはそのまま使いたい」という要求に応えた機能です。


アプリケーションのVer1.00を配置して数日後にバグが見つかったので、すぐさまパッチをあてたVer1.01をリリースしたケースを考えてみてください。バグは膨大なアプリケーション全体から見るとたった1行の勘違いが原因で、修正はファイル1つだけだったなんてこと、よくあることです。

※ネットワークを介するテストはしづらいので初期リリースにはよくありがちなんです。いや、言い訳ですけど・・・。


このアプリケーションをネットワークを介してクライアントにダウンロードさせる。このとき、「アプリケーション全部がダウンロードされる」のと「ファイル1つだけダウンロードされる」のでは、通信帯域の圧迫度合が全然違います。


でも、この差分ダウンロード機能はClickOnceで初めて登場したわけじゃありません。

実は前身技術であるノータッチデプロイメントでも差分ダウンロードはありました。


ノータッチデプロイメントでの差分ダウンロードはHTTPヘッダのIf-modified-sinceフィールドを用いて実装されていますので、ファイルタイムスタンプがちょっとでも違うと、全く同じファイルでも別のファイルとして認識されてしまうという問題がありました。リリース担当者が5月1日に配置したファイルと同一のファイルを5月5日に再配置してファイルタイムスタンプが変わっちゃうと・・・。
あとはよしなに

ファイルが別モノとして認識されちゃいます。

ここまでをふまえてClickOnceの差分ダウンロードを考えてみましょう。


前置き終わり。




ClickOnceで差分ダウンロードがファイルを同一か否かを認識する方法は以下です。

テキトーにアプリケーションを公開してみます。Visual StudioでWindowsアプリケーションをすぐさま発行という手抜きっぷりには目をつぶってくださいな^-^;発行が終わったら、発行されたアプリケーションの「アプリケーションマニフェスト(拡張子manifest)」を開いてみます。


<dependency>
 <dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="WindowsFormsApplication1.exe" size="6656">
  <assemblyIdentity name="WindowsFormsApplication1" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
  <hash>
   <dsig:Transforms>
   <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
   </dsig:Transforms>
   <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1 " />
   <dsig:DigestValue>5D3KU1fjkM9nEjFnBgW6VyZC2q4=</dsig:DigestValue>
  </hash>
 </dependentAssembly>
</dependency>>

エントリポイントに関する記載 WindowsFormsApplication1.exe.manifest(ver1.0.0.0)


DigestValueという項目はメッセージダイジェスト値を示しており、この値は以下の2つの用途に使われています。

・DigestValueが同じファイルは同一として、ダウンロードさせない(差分ダウンロード機能)。
・DigestValueと実際のメッセージダイジェスト値を比較して、異なる場合はダウンロードを破棄(改ざん検知機能)。


ちなみに。

このDigestValueはファイル名に依存しないので、ファイル名が異なってもDigestValueが全く同じなら差分ダウンロード機能が働きます(笑)偶然同じ文字列になることはないでしょうけど、万が一同じになったら誤作動するかもしれませんね。

このようにClickOnceの差分ダウンロード機能ではファイルのタイムスタンプは完全に無視されるため、前身技術であるノータッチデプロイメントの弱点を解決しています。


ではでは、ClickOnceで気にしておきたい2つの問題を考えてみましょう。

これが差分ダウンロード機能の罠にあたります。

たいていの場合はそこまで気にするほどのことではないのですが、ネットワーク負荷を考慮しなくてはならない案件だと面倒かもしれないです。


1.もう一度アプリケーションを発行する。

ここでは、先ほどのアプリケーションをもう一度発行してみます。再度アプリケーションマニフェストを見てください。


<dsig:DigestValue>++BqjwzkX5Kbnkc38eiSntYVzu8=</dsig:DigestValue>

エントリポイントに関する記載 WindowsFormsApplication1.exe.manifest(ver1.0.0.1)


なんだか値変わってませんか?
これがVisual Studioを用いてClickOnceアプリケーションを発行した場合に必ずエントリポイントがダウンロードされてしまう原因です。プログラム上では全く変更されていなくとも、エントリポイントは必ずダウンロードされてしまいます。

この問題は以下の方法で対処します。
回避: .NET Framework SDK(またはWindows SDK)のMage.exeもしくはMageUI.exeを使う。
軽減: エントリポイントのサイズを極力小さくするよう、アセンブリ分割する。
受容: それくらい気にしないと言って無視する(笑) ※結構アリだと思っています。

2.クラスライブラリを追加してみる。
ビジネスロジックやデータアクセスロジックコンポーネントのためにアセンブリ分割したことを考えて見ます。
クラスライブラリを追加して、アプリケーションを再度発行します。

クラスライブラリに関する項目が追加されていますので見てみます。

<dependency>
 <dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="ClassLibrary1.dll" size="4096">
  <assemblyIdentity name="ClassLibrary1" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
  <hash>
   <dsig:Transforms>
   <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
   </dsig:Transforms>
   <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1 " />
   <dsig:DigestValue>WhGfB0gihK5vWsqOFS2zKIPVj3M=</dsig:DigestValue>
  </hash>
 </dependentAssembly>
</dependency>

クラスライブラリに関する記載 WindowsFormsApplication1.exe.manifest(ver1.0.0.2)


ではもう一度発行してみます。

<dsig:DigestValue>WhGfB0gihK5vWsqOFS2zKIPVj3M=</dsig:DigestValue>

クラスライブラリに関する記載 WindowsFormsApplication1.exe.manifest(ver1.0.0.3)


DigestValueが一緒ですので差分ダウンロード機能が働きます^-^
さてさて、次はクラスライブラリをリビルドしてから、アプリケーションを再度発行します。

<dsig:DigestValue>+srR4/OyE5TxgCWYHSrfWGHbUHg=</dsig:DigestValue>

クラスライブラリに関する記載 WindowsFormsApplication1.exe.manifest(ver1.0.0.4)


値が変わりました!
さっきと違うところはプロジェクトを「リビルドした」ということだけ。
これだと差分ダウンロードが働きません。興味のある方は試してみてください。
さらに興味のある方はildasmでアセンブリ内にあるmanifestをDFにかけてみてくださいー。

以上より分かるのは、「アセンブリをリビルドしてしまうと、差分ダウンロード機能は決して働かない」ということです。

この問題、効果的な回避策は今のところ見つけられていません。
しかも、この回避策もせっかく作ったアーキテクチャを壊しがちになるのでここには書きません(というかアーキテクトとしてあまり書きたくない・・・)。

本投稿は、問題提起をした段階で終了することにします。

Visual Studioでソリューションの「ビルド」を選択した際に、「リビルドされる範囲」について考えてみます。
以下のような構成で案件共通アセンブリを変更してしまうと・・・。

構成例) エントリポイント→ビジネス用アセンブリ→業務共通アセンブリ→案件共通アセンブリ

有識者の方からのご意見を頂ければ幸いです。

ClickOnceプログラミング 其の壱

ClickOnceとは、.NET Framework 2.0から利用できるWEBを使ったプログラム配布技術です。


Visual Studioの生産性の高さもあって、IT企業が開発案件に.NETを選択することが多くなってきました。

一言で「開発案件」なんて言っても、その種類は非常にたくさんあります。


ASP.NET Webアプリケーション(型付DataSetを使った)ADO.NET開発は関連書籍も多く、

案件で利用する際に「どうやって実装したらいいか分からない」なんて言うことは少ないと思います。


でも、クライアントに.NETアプリケーションを置き、WebServiceを使ってサーバ通信するという開発体制をとると、

情報源が一気に少なくなってしまう。つまりスマートクライアント開発を採用すると、情報源が少なくなる。


さらに、スマートクライアント開発では必ず「どのようにクライアントに配置するのか」ということが議題に上がる。

昔だったら人を派遣して一個一個インストールしていたようですが、今は違いますよね。

ネットワークを介して配信すれば楽です^-^

ここで使う技術がClickOnceなんですが、このClickOnceに関する情報、特にプログラミング面が少ない・・・。

しかも、このClickOnceという技術には注意すべき点がたっっっっくさんあります。


運用方法次第では差分ダウンロードが無効になるケースだってあるんです。

「ダウンロードグループの設定」と「初回CDインストール」でネットワークトラフィックを軽減しようとしても、

やり方次第で結局大量のトラフィックが発生するケースもあるんです。


ということで、連載形式でClickOnceについて扱ってみようと思います。

もし良かったら読んでやってください。さらに「もっといいアイデア」あれば教えてやってくださいー。


但し、基本的な理論面には触れません。

理論面は@ITで一色さんが「ClickOnceの真実」という記事を公開されているので、こちらを参照して下さい。

<http://www.atmarkit.co.jp/fdotnet/clickonce/index/index.html >

※調査時にものすごくお世話になりました。ありがとうございました^-^


こんな感じで行こうと思いますー。


其の弐 差分ダウンロード機能の仕組みと罠パターン

差分ダウンロードがどういう判定メカニズムなのかを知ってみると、

実は困っちゃうパターンがあることに気づくことができます。


其の参 データディレクトリの仕組みと利用アイデア

データディレクトリに不具合があるからという理由で利用をあきらめていませんか?


其の四 CDからのインストールとダウンロードグループ設定

CDからのインストール時には特別な考慮をしてあげましょう。

CDからのインストールをする方法も紹介します。


其の五 ノータッチデプロイメントとClickOnce

.NET Framework 1.1で利用できるようになったノータッチデプロイメント。

このノータッチデプロイメントとClickOnceについて。


其の六 HTTP圧縮を有効に利用するために

ネットワークトラフィックを軽減するための超必殺技HTTP圧縮。

最近はクライアントマシンにちょっとくらい負荷かけてでも、帯域を守りたいことだってあるんです。

どうやったらいい感じに使えるの?


注)たぶんこの流れで行きますが、勝手に変えることもあります。ご了承ください。



log4net ErrorHandler拡張

C#を使って開発案件に携わるとき、ログのように「毎回使うもの」に困ることが多い。


何度も使ってるのだから見積もりは下げられそうなものだけど、

毎回異なる作りをするから結局工数はそのまま。


ログのオープンソースで成熟しているものはないの?


こういう流れでlog4netを使ってみることになりました。


どうやらstableなのはlog4net 1.2.10のようです。

ここから手に入ります。

http://logging.apache.org/log4net/download.html


ただし、このlog4netに罠が1つある・・・。


log4net内でエラーとなった場合、以下のエラーハンドラが呼ばれること。


log4net.Util.OnlyOnceErrorHandler


だいたい名前から想像できるように、1回しかチャレンジしない。

それ以降は握りつぶされてしまう。


きちんと作ったアプリケーションだと問題ない。

でも、気づかないうちに致命的なエラーを無視しているかもしれない。

log4netの設定が1つ悪いだけで、複数スレッドからのファイルの掴み合いが発生するしね。


ということで、このlog4netの拡張してみましょ。




namespace Yoshinani.Diagnostics
{
  public class YoshinaniErrorHandler : IErrorHandler
  {

    public void Error(string message)

    {

    }


    public void Error(string message, Exception e)

    {

    }


    public void Error(string message, Exception e, ErrorCode errorCode)

    {

    }

  }

}




拡張は、IErrorHandlerを実装したエラーハンドラを作成し、3つのErrorメソッドを定義すれば良い。

実装はお好みでどうぞ。

うちの場合は普段とは別のファイルに保存するロジックを書きました。

この「普段とは別のファイル」に何か書き込まれてたらlog4netの中でエラーが発生しているということ。


作成したら以下のようにConfigファイルに定義してあげる。



<?xml version="1.0" encoding="utf-8" ?>
<log4net>

 <appender name="YoshinaniLogFileApender" type="log4net.Appender.RollingFileAppender">

  <!-- ここに色々な定義をする。ぶっちゃけここの設定が命なので注意してね-->

  <errorHandler type="Yoshinani.Diagnostics.YoshinaniErrorHandler" />
 </appender>

 <root>
  <level value="DEBUG" />
  <appender-ref ref="YoshinaniLogFileApender" />
 </root>
</log4net>