DMMTool(仮名) for Windows開発状況その4
いよいよブックマーク機能を実装しました。
今回は、ブックマーク機能を実装するにあたって利用したMVCという設計手法とXBELという規格の話が中心です。
まずはスクリーンショットをご覧下さい。一般的なウェブブラウザに搭載されているような階層構造を持ったブックマークがウィンドウ左側に表示されているのがわかると思います。
このブックマーク機能の仕組みですが、MVC(Model-View-Controller)という設計手法に則って設計されています。なぜこのような手法にしたかというと複数のウィンドウ間でブックマーク表示の同期を取らなければならないからです。
このツールは一般的なウェブブラウザ同様、複数のウィンドウを開いて作業を行えるようになっているため、あるウィンドウでブックマークの変更を行った場合、他に開いているウィンドウにもその内容が即座に反映させなくてはなりません。
MVCを使わない場合・・・
まず、標準のTreeViewだけで上記の要件を満たしたブックマーク機能を実装することを考えてみます。
TreeViewの表示内容を弄るにはNodesプロパティを使います。ここにTreeNodeオブジェクトを追加したり削除したりすることによりTreeViewの表示内容を弄ることができます。TreeNodeは名前の通りツリー構造になっていて一つ以上の子TreeNodeを追加することができ、これによりデータの階層的な表示を実現できます。つまり、TreeViewは表示に関する機能と、表示するデータを管理する機能の二つを両方持っているということになります。従ってここにブックマークのデータを入れてやればTreeViewを使ってブックマークデータの編集や表示が行えるというわけです。
ところが、複数のウィンドウが開かれていて、それぞれのウィンドウが同じブックマークの内容を表示したい場合はどうでしょう。当然それぞれのウィンドウがTreeViewを持つことになりますがこの場合、変更があったTreeViewの内容を全ての他のTreeViewにコピーして回らなければなりません。
また、あるウィンドウでブックマークの追加や削除が行われたことをどうやって他のウィンドウが知れば良いのでしょうか。また、他のウィンドウで行われたブックマークの変更を自分のウィンドウに反映させるにはどうすれば良いでしょうか。さらに、もっと多くのウィンドウが開かれたり閉じられたりをしていた場合のTreeViewの管理はどうなるでしょう。
おそらくはデリゲート等を用いてウィンドウ間の通知機構を実装する方法にたどりつきますが、考えただけで面倒です。
このような動作をより簡単に実現させるためには、まず表示部分とデータの部分を独立させなければなりません。その上でデータの変更を表示部分に通知するメカニズムと、表示部分に対して行われた操作によって、データの変更を要求するメカニズムを実装する必要があります。これがいわゆるModel View Controllerという設計手法です。
前述の通り、標準のTreeViewだけで上記の要件を満たしたブックマーク機能を実装するのは非常に面倒です。
なぜならWindows.FormsのTreeViewはView(TreeView)とModel(Nodes)が一体になっているため、データと表示部分の分離がされていないからです。
MVCを使うと・・・
そこで、TreeViewには単に表示に専念するだけのViewになってもらい、ブックマークのデータを保持するModelを独自に作ります。
これでViewとModelの分離は行えました。こうするともはやTreeViewのNodesプロパティはブックマークのデータではなく、単なる表示内容のデータになりますから、ブックマークの編集が行われる度にこのデータを他のTreeViewにコピーして回るという面倒なことはしなくても良くなります。
ここでは、ユーザーがブックマークアイテムをあるフォルダへドラッグして移動したケースを例に、MVCがどのように働くかを説明します。
ViewとModelの関係
Modelにはブックマークデータを管理するツリー構造の他に、ブックマークの追加や削除、移動、コピー、名前の変更、フォルダの作成などを行うメソッドを作っておきます。.NET Frameworkには(頻繁に利用されるデータ構造であるにも関わらず)データのツリー構造を管理するクラスが用意されていません。そこでまずはツリー構造を管理するクラスの自作から始めました。
対してViewはWindows標準のTreeViewを用いますが、データの表示に専念させ、データの管理は全く行わないようにします。また、ドラッグ&ドロップやクリック、アイテムの削除の操作は全てコントローラに通知されるようにします。
Controllerの働き
ControllerはユーザーがViewで行った操作に従って、Modelに適切なメッセージを送り、データの変更要求を行う役目を持っています。
上の例ではユーザーがブックマークをドラッグして移動しています。するとこの操作はControllerに通知され、Controllerはその通知に従い実際にモデルに対してデータの変更を要求します。ちなみにControllerはModelのデータを直接いじりません。データの操作を行うのはあくまでModelになります。
ModelからViewへの通知
さて、実際にModelが変更されるとModelからViewへ「Modelの内容が変更されたから表示を更新してください」といった通知が行われます。通知といっても実際にはメソッド呼び出しだったりイベント発生だったりするだけです。ViewはModelの内容を視覚的に認識できる形で画面に表示します。今回の例では、ツリー構造を持ったModelの内容を元に、TreeViewのアイテムの再構築を行っています。
この時大事なことは単一のModelに対してViewは複数存在することができる、ということです。つまり、画面に複数のウィンドウがあり、そこへそれぞれのViewが貼り付けられていたとしても、Modelから各Viewに対して適切に通知が行われれば、あるウィンドウで行ったModelの変更が即座に他のウィンドウのViewにも反映される、ということになります。
複数のViewが存在する場合でも
画面に複数のドキュメントウィンドウが開かれている等、複数のViewが存在する状況でも、単にModelはそれぞれのViewに対して同時にModel更新の通知を送ってやれば、各Viewの表示内容が一斉に更新されるわけです。今回ブックマーク機能の実装にあたりMVCを利用した最大の理由が「場合によっては画面に複数のViewが存在する」という理由です。
ドラッグ&ドロップの実装
今回一番時間がかかったのが、ブックマークのドラッグ&ドロップによる編集です。
TreeViewにはドラッグ&ドロップをサポートする機能が標準で搭載されていますが、若干機能不足です。
たとえば標準のドラッグ&ドロップではTreeNodeを他のTreeNodeの上にドラッグし、ドラッグされたTreeNodeの子とすることは簡単にできますが、あるTreeNodeを二つのTreeNodeの間にドラッグして任意の位置にアイテムの挿入を行う、といった機能はありませんから、こういった機能は自前で実装する必要があります。

また、ドラッグ&ドロップにも下記のようなルールを設ける必要があります。
- あるフォルダをそのフォルダ自身が内包しているフォルダには移動できないようにする。Windowsのファイルシステムを例にあげて説明すると、C:hogeというフォルダと、C:hogehageという二つのフォルダがあった場合、前者のフォルダを後者のフォルダの内部に移動させる操作はできない、といった具合です。
- あるフォルダをそのフォルダ自身の子とすることもできない。
- ブックマークの項目をフォルダへ入れて子とすることはできるが、ブックマークの項目をブックマークの項目の子とすることはできない。
- 同様にフォルダをブックマーク項目に入れて子とすることもできない。
これらの機能を一つ一つ検証しながら実装していたのですが、あまりにもややこしすぎて時間がかかってしまいました。
ブックマークの保存形式
ブックマークの保存は、ブックマーク記述言語「XBEL」形式で行います。XBELとはXML Bookmarks Exchange Languageの略で、XMLを利用したブックマークの相互利用のための規格です。XML形式ですので、ブックマークの保存の際にはブックマークのツリー構造を再帰的に辿ってXmlDocumentを構築した後、ファイルに出力しています。XBEL形式に関してはこのページを参考にしました。
この形式を利用する利点は、様々なブラウザのブックマークとの相互変換が可能である事です。自作のアプリとはいえ、独自のデータ形式を使ってしまっては後々困ることになりますから、オープンな規格はどんどん利用すべきだと思います。
さて、次回はWeb版、デスクトップアプリ版両方のDMMアフィリエイトリンク作成ツールに搭載されているキャッシュシステムについて書いてみようかと思います。
DMMTool(仮名) for Windows開発状況その3
DMMTool(仮名) for Windows開発状況その2
今回もまた、プログラミングに関する記事です。大変申し訳ございません。
前回のエントリを書いてから、空き時間を見つけては地道に作業を続けておりました。
前回からの改善点は
- ネットワークアクセスから何から、ありとあらゆる物をマルチスレッド化した。
- エラー処理の強化
徹底したマルチスレッド化
前回のバージョンではURLボックスにURLを入力して情報取得ボタンを押すと、すべての情報の取得が完了するまでウィンドウがフリーズした状態になっていました。画像が多い場合、全情報の取得には10秒以上かかる場合もあり、その間ユーザーは何の操作もできずに待たねばならなかったのです。これはすべての処理をシングルスレッドで行っていたため、情報取得が完了するまでメインのGUIスレッドが止まるからです。
ところが、たくさんある商品の情報を次々と取得したい場合、ある商品のURLを入力し、表示が完了するまで待ち、次の商品のURLを入力し、また待ち、なんてことはかったるくてやってられません。
そこで、今回はHTMLのスクレイピングから画像の取得まで、すべてをスレッド化し、個別の情報が取得でき次第次々とイベントが発生するように改良しました。発生したイベントはメインのUIスレッドに伝えられ、その度に画面の更新が行われます。この状態でもメインのイベントループが動き続けているため、他の操作を行うことができます。
おかげで、下の画像のようにたくさんのウィンドウを開いて次々と情報取得を行っても非常にぬるぬるとなめらかに動くようになりました。
エラー処理の強化
シングルスレッドで処理を行う場合、極端な話必要な処理を関数として書けるので、どこかでエラーが発生すればその時点で関数を抜けて処理を中断し、エラーが発生したことをユーザーに告知するだけのシンプルなプログラムで済みます。
ところがマルチスレッドの場合には話がすこしややこしくなります。
たとえば平行していくつものスレッドが動いている場合、あるスレッドは正常に完了したが、あるスレッドは途中でエラーが起きた、という状況が起きます。具体的には、複数の画像データをマルチスレッドで平行してダウンロードしていたがある画像のダウンロードだけは途中で失敗した、等のケースです。この場合、あるスレッドで発生したエラーをどのようにメインスレッドに伝えるかが問題となります。.NETではあるスレッドで発生した例外を他のスレッドでキャッチすることができないので、例外が発生した場合にはイベントを発生させてメインスレッドに例外を伝えるということをやっています。
また、エラーの告知方法も少し工夫しました。
このツールでは、ユーザーが商品情報の取得を開始して比較的早期に発生したエラー(URLの入力ミスやパッケージ画像が取得できなかった等)はダイアログを表示してその時点で処理が中断されます。
- ユーザーを完了まで待たせるだけ待たせておいてエラーで処理が中断されてはフラストレーションになるから。
- 取得した画像全てがユーザーが必要としている画像となるケースは希であるから、リロードを行うかどうかはユーザーの判断に任せることにした。
- お気に入り、履歴など
- タブを用いて一枚のウィンドウで複数の商品情報を開けるようにしたい
- 複数の商品のアフィリエイトリンクを効率よく作成できる手段の提供
DMMアフィリエイトリンク作成ツールのWindows版の開発を開始しました。
今日は久々にプログラミングの話題です。
当ウェブサイトで公開しております「DMMアフィリエイトのリンク作成支援ツール」はいわゆるWebブラウザを使ってサービスにアクセスするWebアプリケーションの形になっておりますが、Windows版のツールの開発も開始しました。
まだまだ完成にはほど遠く、公開できる状態にはなっておりませんが、DMMサイトからの商品情報を取得して画面に出力する部分まではうまく出来たようです。Webで公開しているWebアプリケーション版と違い、こちらはサンプル画像の取得もできるようになっています。
当初、画像を一覧形式にして表示する部分は自前で実装してしまおうかと思いましたが、Windowsに標準で搭載されているListViewの使い勝手が思いの外良かったのでこちらを利用しました。ご覧の通り、画像をグループ毎にまとめることができ、これはWindows XPから実装された機能のようで、Windows標準のエクスプローラなどにも使用されています。おかげで自前で画像ビューアを作る手間が省けました。
技術的なこと
以前にもこのブログで書いたことがあるのですが、Webアプリケーション版のツールはPerlやPHP等ではなくC#で開発されています。従ってこのようにWindows版を作ることもすぐに出来るわけです。
私はプログラミングは得意な方ですが、Webデザインに関しては全くの素人で、売れる広告デザインの作り方などはまったくわかりません。そこで、このプログラムがある程度完成したら最初にごく少人数の方にベータテストという形で使って頂き、スタイルシートに関する意見や、プログラムに関する要望などを募集しようと思います。また、このブログのコメント欄などでも、搭載して欲しい機能などの要望を受け付けておりますので、よろしくお願いいたします。
.NET FrameworkのBeginGetResponseメソッドが実行中のスレッドをブロックしてしまう件
数日前の事ですが、当サイトで公開しているDMMアフィリエイトの商品リンク作成ツールの利用者の方から「今までに比べてリンクの作成に非常に時間がかかるようになった」とのご報告を頂きました。
今までは遅くても数秒以内に作成できていた物が、早くても10秒以上かかるというのです。
こちらではしばらくプログラムの変更は行っていませんでしたので、単にDMMのサーバーが重くなっているのではと考えていましたが実は完全に私のミスでした。
何が原因だったのか
当サイトのDMMアフィリエイトのリンク作成ツールでは、開発言語としてC#が用いられています。つまりは、.NET Frameworkの機能を利用して実装されているということです。
プログラム中では、ユーザーの問い合わせに応じてDMMのサーバーから実際に商品情報と商品パッケージ画像を取得するのですが、この時に.NET FrameworkのBeginGetResponseメソッドを用いています。実はこのメソッドにちょっとした問題がありました。
このメソッドはインターネットリソースの非同期要求を開始するメソッドであり、通常このメソッドが呼ばれた瞬間に、要求の完了・未完を問わずに一瞬で呼び出し元に制御が返ってきます。
ところが、条件によってはBeginGetResponseメソッドから制御が帰らずにスレッドが長時間ブロックされたままになってしまうことがあります。私のツールがリンクの作成に非常に時間がかかるようになったのもこれが原因でした。
どんな条件の時にスレッドがブロックされるのか
例によって、海外サイトを検索していたら次のサイトを発見しました。
webrequest.begingetresponse is taking too much time when the url is invalid
http://stackoverflow.com/questions/1232139/webrequest-begingetresponse-is-taking-too-much-time-when-the-url-is-invalid
ところが。
私のプログラムでは無効なURLは指定していませんし、そもそもDMMの商品URLに通常のブラウザからアクセスするとあっという間に結果が表示されます。
しかし、同時にとある事を思い出しました。
実は数日前にApacheの実行ユーザをWindowsサービスのデフォルトユーザから、セキュリティをガチガチに固めた「Apache」というユーザ変更しました。そして改めて検証してみると、Apacheをデフォルトユーザで起動した場合にはリンク作成ツールは今まで通りの速度で動作するのですが、ユーザを「Apache」に変更すると10秒以上の遅延が発生するのです。
ひょっとして、ユーザ「Apache」のインターネット接続設定がおかしいのでは?と思い、ユーザ「Apache」でログオンしていみたところ・・・。
容疑者発見!

「インターネットオプション」を開いてみたところなぜか「設定を自動的に検出する」というチェックボックスがオンになっていました。物は試しにこのチェックボックスをオフにして再度リンク作成ツールをテストしてみると、見事に遅延は解消されました。犯人はおまえだったのか。
参考までに、なんでこのような現象が起きるのかを調べてみたのですが、マイクロソフトの英語版公式リファレンス によると、こんな記述がありました。
The BeginGetResponse method requires some synchronous setup tasks to complete (DNS resolution, proxy detection, and TCP socket connection, for example) before this method becomes asynchronous. As a result, this method should never be called on a user interface (UI) thread because it might take some time, typically several seconds.
要約:このメソッドは非同期状態になる前に、いくつかの同期タスク(例えば、DNS解決、プロキシ検出、TCPソケット接続等)を完了する必要があり、場合によってはそれらの同期タスクには数秒を要するため、このメソッドはUIスレッドから呼び出されるべきではありません!
なお、日本語版のリファレンスには載っていませんでした・・・。
決だけではなく、プロキシ検出なども含まれるため、「設定を自動的に検出する」オプションにチェックが入っているにもかかわらず設定の検出に失敗したり時間を要した場合には、その間スレッドはブロックされてしまうっぽいです。
DMMアフィリエイトのリンク作成ツールをバージョンアップしました。
当サイトで公開しております、「DMMアフィリエイトのリンク作成支援ツール」ですが、本日若干の改良を加えました。
従来はテキストリンクかイメージリンクのいずれかしか作成できませんでしたが今回新たに「イメージ+商品説明テキスト」というスタイルでアフィリエイトリンクを作成する機能が追加されました。
なお生成されるリンクの見た目はスタイルシートでカスタマイズ可能となっております。
外枠の色を変えたい、テキストの大きさや色を変えたい、などの要求にはできるだけ応えられるようになっております。
DMMアフィリエイトのリンクを簡単に作成できるツールを公開しました。
お久しぶりです。
前回のエントリで「面倒な日常作業を自動化するプログラムを書いている」とお伝えしましたが、たった今完成しました。これは自分のためでもあるのですが、知人に「どうしても作ってくれ!」と頼まれた部分もありまして、プログラムを書いておりました。
このツールはDMMで販売されている商品ページのURLを最大20件まで入力でき、一括でアフィリエイトリンクを作成する事ができるツールです。
ツールはこちらのページで公開しております。
早速使い方を説明致します。
アフィリエイトIDを入力
まず、あなたのアフィリエイトIDを入力します。
![]()
商品のURLを入力(複数入力可)
続いて、アフィリエイトのリンクを作成したい商品のURLを入力します。
複数のURLがある場合には改行で区切って入力します。最大20件まで入力できます。
![]()
オプション選択
次に、リンクの作成オプションを選択します。テキストリンクか画像リンクかを選択できるほか、サムネイルイメージのリサイズも行えます。
![]()
結果の確認
最後に、送信ボタンを押しますと、生成されたリンクタグや実際にブラウザでどう見えるかがプレビューに表示されます。
![]()
おまけ
なお、画像リンクを作成した場合、画像の上にマウスを載せると商品の説明がポップアップするように出来ています。
![]()
技術的な事
このツールはCGIとして動作するツールですが、ほぼすべてのプログラムがC#で記述されており、.NET Frameworkの機能を活用して作られています。従って、Windows版を作るのはとても簡単、というより、実は既にWindows版も存在します。Web版のURL20件制限はサーバーの能力が乏しいために存在しますが、Windows版には制限はございません。ただし、Windows版はまだコア部分に適当なGUIをかぶせただけで、とてもじゃないけど公開できる代物ではないためまだ公開はしておりません。
なお、DMMからデータを取得する際、一度取得したデータはこちらにキャッシュされますので、必要以上にDMMのサーバに負荷をかけるという事はありません。
久々にプログラミング中です。
これからブログの更新がすこし遅くなりますが、お許しください。
私がプログラムを書く目的の一つに、常日頃行っている面倒な作業を自動化するという目的があるのですが、そのプログラムを作るほうがよっぽど面倒で、しかも孤独な作業であるため、今にも発狂しそうです。
しかし、ひょっとしたら他の方にも需要があるかもしれないため、近日中にこのサイトで公開しようと思います。
GoogleのJavaScriptオプティマイザ「Closure Compiler」
http://code.google.com/intl/ja/closure/compiler/
What is the Closure Compiler?
The Closure Compiler is a tool for making JavaScript download and run faster. It is a true compiler for JavaScript. Instead of compiling from a source language to machine code, it compiles from JavaScript to better JavaScript. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what’s left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls.
意訳:
Closure Compilerとは何か。
Closure CompilerはJavaScriptの読み込みと実行を高速化するツールである。これは正真正銘のJavaScriptコンパイラであるが、一般的なコンパイラがソースコードを機械語に翻訳する代わりにClosure Compilerは既存のJavaScriptコードをよりパフォーマンスに優れたコードへと変換する。Closure Compilerは最初にJavaScriptの構文解析と分析を行った後、不必要なコードを削除し、残りのコードをより小さなコードへと書き換える。また、文法や変数の参照、型などにいても検査し、JavaScriptにおいて陥りやすい問題について警告する。
コンパイラというよりは、オプティマイザですね。
I’m Jugglamp EXは64ビット版のWindows では動作しないそうです。

Windows 7 @ ウィキ – 対応ソフト(x64)より。
誰が調べてくださったのかはわかりませんが、非常に感謝しています。
できれば、どんなエラーが表示されたかがわかると修正の手がかりになります。
ちなみに私は開発者のくせに64ビットOSを持っていないので、動作確認が行えません。ごめんなさい。
※プログラム全体でプラットフォーム依存している部分はほとんどないのですが、おそらくDirect Xをいじってる部分か、乱数を生成している部分で、わりとハードウェアに近いWin32 APIを呼び出しているのでそこでひっかかってるのかな?
※追記
64ビット版のWindowsで動作しない原因と思われる箇所を修正したバージョン2.0.4を公開しました。しかし私は動作確認環境を持っていないため、動作の確認が出来た方はコメントをいただけると助かります。
