ClickOnceプログラミング 其の弐
ClickOnceプログラミング 其の弐 「差分ダウンロード機能の仕組みと罠パターン」
初回は「差分ダウンロード機能」について掘り下げてみます。
では、ちょっとだけ前置きします。
差分ダウンロード機能とは、「変更されてないファイルはそのまま使いたい」という要求に応えた機能です。
アプリケーションのVer1.00を配置して数日後にバグが見つかったので、すぐさまパッチをあてたVer1.01をリリースしたケースを考えてみてください。バグは膨大なアプリケーション全体から見るとたった1行の勘違いが原因で、修正はファイル1つだけだったなんてこと、よくあることです。
※ネットワークを介するテストはしづらいので初期リリースにはよくありがちなんです。いや、言い訳ですけど・・・。
このアプリケーションをネットワークを介してクライアントにダウンロードさせる。このとき、「アプリケーション全部がダウンロードされる」のと「ファイル1つだけダウンロードされる」のでは、通信帯域の圧迫度合が全然違います。
でも、この差分ダウンロード機能はClickOnceで初めて登場したわけじゃありません。
実は前身技術であるノータッチデプロイメントでも差分ダウンロードはありました。
ノータッチデプロイメントでの差分ダウンロードはHTTPヘッダのIf-modified-sinceフィールドを用いて実装されていますので、ファイルタイムスタンプがちょっとでも違うと、全く同じファイルでも別のファイルとして認識されてしまうという問題がありました。リリース担当者が5月1日に配置したファイルと同一のファイルを5月5日に再配置してファイルタイムスタンプが変わっちゃうと・・・。
ファイルが別モノとして認識されちゃいます。
ここまでをふまえてClickOnceの差分ダウンロードを考えてみましょう。
前置き終わり。
ClickOnceで差分ダウンロードがファイルを同一か否かを認識する方法は以下です。
テキトーにアプリケーションを公開してみます。Visual StudioでWindowsアプリケーションをすぐさま発行という手抜きっぷりには目をつぶってくださいな^-^;発行が終わったら、発行されたアプリケーションの「アプリケーションマニフェスト(拡張子manifest)」を開いてみます。
<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.もう一度アプリケーションを発行する。
ここでは、先ほどのアプリケーションをもう一度発行してみます。再度アプリケーションマニフェストを見てください。
エントリポイントに関する記載 WindowsFormsApplication1.exe.manifest(ver1.0.0.1)
なんだか値変わってませんか?
これがVisual Studioを用いてClickOnceアプリケーションを発行した場合に必ずエントリポイントがダウンロードされてしまう原因です。プログラム上では全く変更されていなくとも、エントリポイントは必ずダウンロードされてしまいます。
受容: それくらい気にしないと言って無視する(笑) ※結構アリだと思っています。
2.クラスライブラリを追加してみる。
ビジネスロジックやデータアクセスロジックコンポーネントのためにアセンブリ分割したことを考えて見ます。
<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)
クラスライブラリに関する記載 WindowsFormsApplication1.exe.manifest(ver1.0.0.3)
クラスライブラリに関する記載 WindowsFormsApplication1.exe.manifest(ver1.0.0.4)
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からのインストールをする方法も紹介します。
.NET Framework 1.1で利用できるようになったノータッチデプロイメント。
このノータッチデプロイメントとClickOnceについて。
ネットワークトラフィックを軽減するための超必殺技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>