「初めてのWindowsガジェット」ソースコード解説

【ランチタイムラーニング】 初めてのWindowsガジェット」という記事(冒頭の「注」もお読みください)で、お昼を食べながら踏み出す、ガジェット作成の第一歩について説明しました。

この記事では、その記事に掲載したソースコードについて、少し詳しく解説します。

JavaScriptでのタイマー機能の実装

最初はHTMLの開始と、CSSのスタイルの定義です。

7行目で、bodyの幅を130pxに設定しています。ガジェットがWindows Vistaのサイドバーに貼り付いている状態を「ドッキング」と言いますが、このドッキングの状態では、ガジェットの最大の幅は130pxです。

また、ガジェットをサイドバーに貼り付けず、単独でデスクトップに表示する状態にすることができ、この状態を「アンドック」と言います。

アンドックした状態では、ガジェットの幅は130px以上に設定することができます。多くのガジェットでこの設定を利用して、ドッキングの状態では簡易的なビュー、アンドックした状態ではリッチなインターフェイスを持ったビュー、というようなビューの切り替えを提供しています。ただし、アンドックした状態でも、ガジェットのサイズは縦横共400pxを超えないことをMicrosoftは勧めているそうです。

さて次にHTMLを見てみましょう。

4行目が経過時間を表示するエリア(nowTime)です。そして、5行目に設定時間を入力するフィールド(setTime)があり、6行目、7行目がタイマーを開始、停止するためのボタンです。開始するにはJavaScriptで記述したstartTimer()ファンクション、停止するにはfinish()ファンクションをそれぞれ呼ぶようになっています。

1行目で、このHTMLがロードされたときにJavaScriptで記述したinit()ファンクションを呼ぶようになっていますが、後で出てくるように、今回のガジェットではこのinit()ファンクションは何もしないので、このHTMLがロードされたときは特別な処理は行われません。しかし、今後タイマーの背景画像を変えたりするなどカスタマイズするときに、HTMLがロードされるタイミングでJavaScriptのファンクションを呼ぶ、ということを覚えておきましょう。

最後にJavaScriptを見てみましょう。

ここでは経過時間の常時を更新し(26行目)、毎秒JavaScriptのsetTimeout()でつぎのちょうどの秒にdoSecTimerを呼び出します(30行目)。念のため高負荷がかかっていて表示の次のちょうどの秒が過ぎてしまった場合には単に0.1秒後に再度更新するようにします(32行目)。これをsetTimeout()で設定時間までの時間を計る(39行目,44行目)ことでタイマーを実装しています。

表示の更新は、その時点での時間と開始時間の差分をとって、何秒経過したかを計算します。もちろん1秒ごとに1を足すのが一番簡単ですが、タイマーイベントがジャストの時間に発生しなかったり、そのほかの計算での時間も足されてしまうのでどんどんどんどん遅れることになり、やめたほうが良いでしょう。

また、前述の通りこのガジェットではHTMLがロードされたときに呼ばれるinit()ファンクションでは何もしませんが、7行目、8行目でコメントアウトしてある行のコメントをはずして、背景画像を表示することができるでしょう。

ガジェットでのalertの実装

さてタイマーはこれで実装できるのですが、そもそもタイマーは、指定時間が経過したことを「ぴぴっ」とか言いながら教えてくれてこそ、その本領を発揮できると言うものでしょう。なのにこのタイマーにはアラーム機能がありません。まるで人ごとのように書いてしまいましたが、このセクションでは簡単なアラーム機能を実装しましょう。

そもそも何故このタイマーガジェットはalert()ファンクションを使って、ひょいっとポップアップさせることさえしないのか。それは、alert()ファンクションやconfirm()ファンクションで出てくるポップアップがWindows Vista User Experience Guidelines for the Sidebarに則っていないということでサイドバーガジェットでは無効化されているからです。

それでもとりあえず簡単にポップアップを出したいときもあるではないですか。デバッグするときなんか特に。MSDN Magazineの記事に、そんな開発者の心の叫びに応えた、alert()やconfirm()と同等の機能を実装するやり方(ガジェットプラットフォームが脆弱である一因)が掲載されています。以下が今回のタイマーガジェットにそのやり方を適用する方法です。

ステップ1: ファンクションを準備する

作業フォルダに新しく「Timer.vbs」というファイルを作成して、次のように記述し保存します。

ステップ2: タイマーガジェットから呼び出す

Timer.htmlの中で、前述のCSSの直後、JavaScriptの直前に次の行を書きます。

同じくTimer.htmlの中の、finish()ファンクションの最初か最後に次の行を書きます。

これで簡単なアラーム機能ができました。

参照情報

【ランチタイムラーニング】 初めてのWindowsガジェット – お昼を食べながらガジェットを作ってみよう – Windows 7 デスクトップおよびVista サイドバー

「ガジェット」という言葉を聞くと、昔のアニメ、Inspector Gadgetの決め台詞、”Go-Go-Gadget!”と叫びたくなります。この台詞と共に、体のあちこちからわらわらとガジェットが出てくる様は、さながら洋風ドラえもんといったところです。

GUIを伴うアプリケーション作成は数年前まではちょっと敷居が高いものでしたが、現在ではエディターと初歩的なHTMLやJavaScriptの知識があれば、便利な「ガジェット」がいとも簡単に作れるようになりました。しかも作ったガジェットを世界中の人たちと共有できる環境があるので、その便利さや自分の作品を公開し使ってもらえる楽しさから、文字通り老若男女が挑戦しています。

最初の一歩はお昼を食べながら踏み出せるくらいの手軽さです。明日のお昼にでも、ちょっと作ってみませんか?


注: 脆弱なガジェットをインストールした場合に遠隔からコードを実行させることができてしまうプラットフォームである(KB2719662)との理由でWindows 8以降での機能実装の打ち切り、およびガジェットを無効にするように薦めています(理由は不明ですがガジェットを無効にするためのお手軽Fix it 50906の提供が中断か終了したようなので、無効にする場合は上記記事の中のSuggested Actionsの抜粋 Windows サイドバー ガジェットの悪用から Windows Vista および Windows 7 コンピューターを保護する方法 を参照してください)。ガジェットはお手軽ですが、動作させることはexeを動作させているのと同じ危険性を持つので、由来のわからないものや、由来がわかっていても脆弱かもしれないという意識が必要です。またローカルファイルにアクセスする権限やコードの実行権限があるので、脆弱なものを作ったりインストールしたりしたら大変なことに、しかも管理者権限でログインしていたらOSまるごと乗っ取られるかもしれませんよ、ということになります。以上、当たり前な話であるわけですが、テキストエディタだけでお気楽に作れる分余計に注意が必要、ということでしょうか。メールに添付されたzipの内のjsファイルを実行してしまってファイルを軒並み暗号化されて、復号のためにはbitcoin払えと表示されて涙、と似たようなものです(こちらはOSによる注意のダイアログが出る分少しましかも知れないですが、見慣れていて結局そのままOK押してしまうかも)。


何を作るか

Windows サイドバー ガジェット開発者ガイドには、”Hello, World!”と出てくるガジェットの作り方が掲載されています。”Hello, World!”はチュートリアルの王道ではありますが、もう少し実用性のあるガジェットを作ったほうがより楽しく作れるでしょう。

例えばタイマーなどは便利な上に簡単です。最近、仕事を効率よく進めるコツとして「ライフハック」という言葉が使われていますが、いくつかのライフハックの中で1日の時間を分割して効率良く作業を進める方法を取り上げています。ただ、時間を気にしながら作業をしても集中できないので、タイマーがあると便利です。それにタイマーはエンジニアの友であるカップ麺を作る時にも重宝します。

そこで今回は簡単なタイマーを自作します。この簡単なタイマーを改造していけば、自分好みのタイマーを作ることができます。

材料を準備しましょう

【材料】

  • お気に入りのエディタ (Windowsのメモ帳(notepad.exe)で十分)
  • ブラウザ

以上です。そうです、今この記事をご覧になっているのであれば標準的にコンピュータに入っているもので作れてしまうのです。それがガジェット作りの手軽さなのです。

もちろんJavaScriptやHTMLのリファレンスが手元にあると便利ではありますので、お好みでご利用ください。

さっそくガジェットを作りましょう

手順と成果物の概要

ではさっそくガジェットを作りましょう。その手順は以下の通りです。

  1. %LOCALAPPDATA%\Microsoft\Windows Sidebar\Gadgetsにフォルダーを作る→「○○.gadget」フォルダー
  2. JavaScriptを含んだHTMLを記述する→「○○.html」ファイル
  3. マニフェストを記述する→「gadget.xml」ファイル

以上、簡潔な3ステップです。

ステップ1: 作業フォルダの作成

%LOCALAPPDATA%\Microsoft\Windows Sidebar\Gadgets (C:\Users\<ユーザー名>\AppData\Local\Microsoft\Windows Sidebar\Gadgets)に「Timer.gadget」フォルダー(以降、作業フォルダーと呼びます)を作成します。

ステップ2: JavaScriptを含んだHTMLを記述する

作業フォルダーに、新しく「Timer.html」というファイルを作成して、次のように記述し保存します。(このHTMLファイルの内容の詳しい解説は、『ソースコード解説』に記述しましたので、必要に応じて参照してください。)

ステップ3: マニフェストを記述する

作業フォルダーに新しく「gadget.xml」というファイルを作成します。

作成したファイルに次のように記述し保存します。Shift_JISの代わりにUTF-8で保存できるエディタの場合はencoding=”…”の部分は不要です。

ガジェットを動かしましょう

Windows Vistaの場合

サイドバーを右クリックで「ガジェットを追加」を選びます。

Windows 7の場合

Windows 7の場合はデスクトップの右クリックで「ガジェット」を選びます。

するとガジェットのリストに、「Simple Timer」が出てくるので、それを選びます。

パッケージを作る場合

作成した「Timer.html」「gadget.xml」の2つのファイルをzipにまとめます。zipにフォルダ名が保存されないように(zipファイルをダブルクリックした際に、フォルダではなくて2つのファイルが見える状態)、作成してください。そしてできあがったzipファイルを、「Timer.gadget」という名前に変更します。出来上がった「Timer.gadget」ファイルをダブルクリックしてインストールできるパッケージができます。

さあ、いかがですか?ちょっと見た目が悪いタイマーっぽいものが出てきましたね?

そして自分好みのタイマーに

今回作成したタイマーは、ガジェットの作り方の基本だけなので装飾していません。自分好みの背景画像や、色、フォント、さらにアニメーションを加えて、自分好みのタイマーに育ててみましょう。それがアプリケーション作りの醍醐味のひとつです。

また、このタイマーには指定時間が経過したことを通知するアラーム機能がありません。簡単なアラーム機能に関しては、上記のHTMLファイルを解説した「『初めてのWindowsサイドバーガジェット』ソースコード解説」という記事に記述しましたので、必要に応じて追記してみてください。

そうしているうちにガジェットのJavaScriptは原型を留めないほど変わるでしょう。その頃にはタイマーに限らず、自分の好きなガジェットが作れるようになっているはずです。楽しいガジェットを作って、世界に向けて発信しましょう!

参照資料

ばっちり書けてますかhref= 特に&(&amp;)などを含む場合 属性値としてのCDATA

<a href=”xxx”>や、<param name=”FlashVars” value=”xxx”>のxxxに&(ampersand アンパサンド)が含まれる場合、正しい書き方ができていますでしょうか?


追記(2016/8/31)

最近ではAccelerated Mobile Pages(AMP)を有効にするパラメータとして本当にampが使われてamp=1とかを利用するプラグインなどが出てきました。今時ではXHTMLも使わないでしょうし&amp + ;以外の記号 でもエラー部分を消さずに &amp;amp + ;以外の記号 相当とみなしてパースが進行するようになっているので気にする必要もないかもしれません。

  • <link rel=”amphtml” href=”/hoge?amp=1″ /> OK
  • <link rel=”amphtml” href=”/hoge?param1=1&amp;amp=1″ /> OK
  • <link rel=”amphtml” href=”/hoge?param1=1&amp=1″ /> NG

とはいえ、amphtml以外のpathが/ampで終わらないようにできるのであれば、 /amp を query string の amp=1 に、例えばmod_rewriteならば

のようにwebサーバ側でrewiteする方が気にすることも減って無難かもしれません。

追記終了


複数の変数/パラメータを渡したい場合、その変数/パラメータの名前がampだったり、値に&が含まれている場合を考えていきます。

書くまでも無い当たり前なネタなのかもしれませんが、AdobeサイトのFlashVars解説にも誤記が あったり、「表示」⇒「ページのソース」だけで学んでいる人は気づかなかったりするかもしれないので、関連仕様書を一まとめにしておく意味でも書いておき ます。大きな問題であるわけでもなく気にしなければしないですませられますが、実体参照と同じ名前の変数を使いたい場合のように機能的に困る場合があります。


クイズです。次のHTMLをノートパッドなどのテキストエディタで作成してhoge.html等適当な名前で保存します。

次にこのファイルをお好きなwebブラウザでこのHTMLファイルを開き、アンカーのリンク先を右クリック等ででクリップボードにコピーし、テキストエディタにペーストします。どうなるか予想できましたでしょうか。

Windowsの実験機で試した結果は以下の通りです。

FF3⇒notepad file:///?hoge=123&=567
Safari4⇒notepad file:///?hoge=123&=567
Opera10⇒notepad file://localhost/?hoge=123&=567
IE8⇒notepad file:///D:/?hoge=123&=567
IE7⇒notepad file:///D:/?hoge=123&=567
IE6⇒notepad file:///D:/?hoge=123&=567
IE5.5⇒notepad file:///D:/?hoge=123&=567

hostのあるものやpathにドライブのあるものなどいろいろ面白いですが、今回の本題は123の右です。どこにもampがありません。これは「不幸に も多くのHTMLユーザエージェントは変な実体参照でも適当に問題をシカトして先に進んでしまう。(下記参照)」からです。&amp=の部分を& amp;quot=や&copy=などに変えたり、先頭に

を付けたりして(ローカルのファイルでもXML宣言を見てこれをエラーにするブラウザは有りましたか?)いろいろ試してみてください。quotやcopy等では&すら出てこなくなるので間違いにすぐに気づくかもしれません。今まで気づいていなかった方にはとても違和感があるかもしれませんが、パラメータampに値567を渡したい場合の正解は

になります。Flash PlayerにFlashVarsを<object>の中の<param>で渡したい場合はどうでしょうか。HTMLやXHTMLの世界での話なので、変数p1,p2,ampにそれぞれ値を渡したい場合は

といったものが正しい書き方になります。あとは=前後の%エンコード(Percent-Encoding)(FlashVarsの説明ではURL encodingと書かれていますが、各種のURLencode()のような関数やapplication/x-www-form-urlencodedで使われるエンコードではあの記号はそのまま、この記号は%エンコードという奇々怪々な仕組みも含まれる場合があるので素直に%エンコードと呼び、かつFlashVarsの値はECMAScriptのencodeURIComponent()相当の符号化、つまりUTF-8以外の場合はUTF-8に変換をしてから英数以外は積極的に%エンコードする、というのがさまざまな場合で無難だと思います)もお忘れなく。具体的にはもしFlashVarsで変数p1,p2,ampに&が含まれている値を渡したい場合は次のようになります。(%26はUS- ASCIIのスーパーセットの文字セット-文字符号化では&になります。)

変数側に記号を入れてみたりするのも面白いかもしれません(例外的にNULは文字列終端扱いである模様で、例えば

ではswfのhogeにheroが渡されました。実装された詳細仕様は不明です)。href=での%エンコードの話は、サーバ側、アプリケーション側がどうなっているかに依存する、つまりwebサーバやアプリケーションサーバ、CGIもからんできて、単純にこうだとは書くことができません。

では

のように、HTMLとしては期待されていない処理を誘発し、XHTMLとしてはエラーになる属性値(value=の部分)の場合、swfに何が渡ってくるでしょうか(IEはobject要素がparam以外の要素を持てないのでコメント要素は取り除きます)。Flash Player 10.0.42.34との組み合わせで以下の通りです。もっとも、HTMLを解釈してFlash Playerに渡すまでの作業はwebブラウザ(HTMLユーザエージェント、HTMLパーサ)の仕事であり、もしFlash Playerのバージョンで変わるとすればそれはFlashVars自体の仕様の変化となります。また、XHTMLとしてちゃんとエラー扱いになった場合はそもそもFlash Playerが起動しません。

FF3 swfのampの内容は空
Safari4 swfのampの内容は空
Opera10 swfのampの内容は空
IE8 swfのampの内容は空

同時に&amp;amp=bazで正しくswfのampにbazが渡ってくること、そして残念ながら世の中に氾濫している&xxx=bazでも、エラー扱いではなくかつxxxが文字実体参照や数値文字参照にぶつかっていないものはbazが渡ってくることも確認しました。上の href=と同じ状況で予想通りです。

ところでこれはHTMLやXHTMLの世界での話なので、HTML内での記述ではなくて別の外部ファイル(.js等)に分けて、

などで提供される分にはまったく当てはまりません。パラメータhoge,p1,p2,ampにそれぞれ値を渡したい場合はそのまま

というような文字リテラルでOKです。

HTML内でのonclick=などの属性値ではContent-Script-Typeがapplication/javascript(text/javascript)の場合

のようになります。<script>で記述する場合は内容モデルがHTMLではCDATA, XHTMLでは#PCDATA(下記参照)という別な話が登場して終わらなくなってしまいますので機会があれば別途。とにかく外部ファイルにしてしまうと 決めていれば稀な場合でも大きな問題にならないはずです。

優れたバリデータは文法エラーは当然のこと、誤った処理をさせる部分を検出して警告してくれるので活用されると良いかもしれません。

なお、弊社遊び場の中のFlashアプリの ページでは、ActiveX用とNetscape式plugin用さらにはSWFObject用に同じパラメータを何度も書かなければいけないという問題 とSWFObjectだけではJavaScript依存にしてしまうという問題を解決し、かつActiveXでなくても古いFlash Playerが入っていればExpress Installationが起動するというちょっと素敵なやり方をしています。機会があればご紹介します。

長くなりましたが、以下に根拠となる仕様書(一部ですが)のリンクと、簡単な説明をまとめてみました。ぜひご自身でも読み解いて、上に書かれていることが本当か確認してください。間違いがあったらぜひコメントをお願いします。

<param>要素のvaue=属性の値に関する仕様書の一部の超訳

<a>要素,<link>要素のhref=属性の値に関する仕様書の一部の超訳

属性値の場合のCDATAに関連する仕様書の一部の超訳

  • HTML 4.01:CDATAは文書文字集合のシーケンスで、文字実体を含むことができる。ユーザエージェントは属性値を次のように解釈するべきである。文字実体を文字で置きかえる。LFを無視する。CRやTABは各々SPに置き換える。: http://www.w3.org/TR/1999/REC-html401-19991224/types.html#h-6.2
  • HTML 4.01(SGML tutorial):属性値はダブルクォートかシングルクォートで括る。決められた文字だけの場合は括らなくてもいいけど。数値文字参照を利用し て"でダブルクォート、'でシングルクォートを値に含められるし、ダブルクォートで括られている場合は文字実体参照を利 用して&quot;でシングルクォートを値に含められる。: http://www.w3.org/TR/1999/REC-html401-19991224/intro/sgmltut.html#h-3.2.2
  • XML 1.0: ダブルクォートかシングルクォートで括る。<と&と括り始めのクォート以外の文字、あるいは「参照」で構成される。: http://www.w3.org/TR/2008/REC-xml-20081126/#NT-AttValue
  • XML 1.0: 参照は、実体参照(例:&quot;)か、文字参照(例:",")である。: http://www.w3.org/TR/2008/REC-xml-20081126/#NT-Reference
  • XHTML 1.0: SGMLでもXMLでも&(ampersand character)は実体参照の開始宣言であるが、不幸にも多くのHTMLユーザエージェントは変な実体参照でも問題をシカトして先に進んでしまう。 XMLユーザエージェントはこの不正な使用を認められない: http://www.w3.org/TR/2002/REC-xhtml1-20020801/#C_12
  • HTML 5 (右は訳ではなくて注釈):今までのHTMLではそのものすばりと書かれていなかったようだけど、ついにどうやって間違った文法(前出のHTMLの場合で は&amp=を&ampy=にしたり、&copy=を&copydayo=にするといった悲しくも不正な回避方法が使わ れてしまった場合)をパースし続けるかが定義(恐るべし膨大なレガシー資産パワー!)される模様。: (このリンク先はレンダリングするのに結構リソース消費します。クリックするときは注意してください)http://dev.w3.org/html5/spec/Overview.html#tokenizing-character-references

<script>要素に関する仕様書の一部の超訳(参考)

要素内容の場合のCDATAに関連する仕様書の一部の超訳(参考)

Adobe.comでの説明(頻繁にURLが変わりpermalinkも無いのでたまに修正していますがリンク切れの場合がしょちゅうあります)(参考) (サンプルは適切でないです。修正されることはなさそうです。)

  • TechNote (Knowledge Base): <PARAM NAME=FlashVars VALUE=”foo=Hello%20Worldgraph=first+line%0Dsecond+line”> の部分はおそらく VALUE="foo=Hello%20World&amp;paragraph=first+line%0Dsecond+line" の意図で書かれたものと思われますが、graphの前に&すらなくなってしまっているのでサンプルの意味がとてもあいまいになってしまっています(2010/1/14時点)。Macromediaからのお引っ越しの際にでも発生したエラーでしょうか: Using FlashVars to pass variables to a SWF
  • Flex 4: この説明文の<param>等のHTMLでFlashVarsを使用しているもののうち、&lastname=の部分(2010/1 /14時点)は、勝手にlastnameが文字実態参照に使われないことを暗黙のうちにリザーブしています。lastname部分をampやquotやcopyやnbspやltやgtや…にした場合は動作しないことを記載するか、あるいは普通にXHTMLでもHTMLでもampやquotやcopyという変数を渡したいときでも百万が一にも文字実体参照に&lastname;が追加された場合も使えるサンプルにするためにも、&amp;lastname=を使用するのが妥当と考えられます: Passing request data with flashVars properties – Communicating with the wrapper
  • Developer Connection: ユーザの入力を動的にFlashVarsに渡すような場合、%エンコードすることの重要性やURLのような場合はスキーマやホストの確認なども重要であることが書かれています。一読をお勧めします。: Creating more secure SWF web applocations

2010/4/13 文章校正(項目の順序入れ替え)