SyntaxHighlighter

2011年12月30日金曜日

NPAPI プラグインの作り方 for Windows その4

NPAPI プラグインの作り方 for Windows その4

ずいぶん更新が止まってしまいましたが、NPAPI プラグインの作り方の続きです。

今回は、サンプルが動くようにしてみます。

なお、今回のサンプルは、GitHub のページからダウンロードできます。

サンプルの動作のさせ方

まず、前回作成したサンプルの NPAPI プラグインを動作させてみます。

ただし、もともとのサンプルのプラグインのバグが原因で、まだ正しく動作しません。

プラグインの登録

Firefox の場合

Firefox の場合は、インストールされたディレクトリ (C:\Program Files\Mozilla Firefox など) の下に plugins というディレクトリがあるので、その下に作成した DLL をコピーします。

これで、次回以降の起動でプラグインが動作するようになります。

Google Chrome の場合

Google Chrome の場合は、拡張機能を作成するのが簡単だと思います。

Google Chrome の拡張機能の作り方は、Chrome Extensions API リファレンスなどを参照してください。

バックグラウンドページのみを持つような拡張機能を作成し、以下のような plugins 属性を持った manifest ファイルを作成します。

publictrue にしておくと、プラグインをどのページからでも参照できるようになります。

ただし、これはセキュリティホールになることが多いので、テスト環境のみにした方が無難だと思います。

「ツール」-「拡張機能」を開き、デベロッパーモードにチェックを入れた上で「パッケージ化されていない拡張を読み込む...」で読み込むと、プラグインを使えるようになります。

テスト用 HTML の用意

前回の Firefox の公式ページのサンプル npruntime には、テスト用のファイル test.html が含まれていますので、これをブラウザで開きます。

画面の下の方にはいくつかボタンが並んでいると思いますが、これらを押すと、本来ならメッセージがポップアップ表示されるはずです。

しかし、ページ読み込み時かボタンクリック時に、ブラウザが「プラグインがクラッシュした」というようなメッセージを出してくると思います。

そうならない場合は、プラグインが正しく読み込まれていない可能性が高いので、手順を確認してください。

なお、test.html 内には二つの embed タグがありますが、今回作成したサンプルは、一つ目の embed タグに対して読み込まれます。

二つ目の embed タグに関しては「対応するプラグインが見つかりません。」のように表示されたままですが、今回は特に対応したりはしないことにします。

GitHub に用意しておいた test.html から、二つ目の embed タグは削除してあります。

サンプルの修正

ブラウザがクラッシュしないように修正します。

修正するのは plugin.cpp 内の3箇所です。

GibHub の diff が見易いのでこちらを参照した方がよいかもしれません。

452行目付近の _strdup
以下のように、*result_strdup の結果を代入するのではなく、NPN_MemAlloc でメモリを確保した上で *result に代入してやります。
466行目付近
こちらも、まったく同様に _strdup によるコピーを修正します。
540行目付近
以下のように、NPN_Evaluate の最後の引数に変数を渡してやり、戻り値を受け取れるようにします。
この値は不要なので、忘れず NPN_ReleaseVariantValue で解放してやります。

この状態でコンパイルしなおし、DLL をコピーした上で、もう一度サンプルの test.html を閲覧すると、クラッシュせずに動作することが確認できると思います。

次回は、このサンプルにディスクサイズを取得するメソッドを追加し、もう少しサンプルの動作を説明する予定です。

2011年12月12日月曜日

Redmine Plugin で ApplicationHelper を拡張する際の注意

Redmine Plugin で ApplicationHelper を拡張する際の注意

Windows PC の調子が悪いので、NPAPI プラグインの作り方は少し延期です。

今回は、Redmine 1.3.0もリリースされたので、 Redmine Plugin で ApplicationHelper を拡張する際に悩んだ問題について書きます。

Redmine プラグインで、既存のクラスやモジュールを拡張する

Redmine プラグインを作成し、 ApplicationHelper などの既存のクラスやモジュールを拡張する場合、基本的には以下のようにします。

  1. プラグインのlib以下にredmine_sample_plugin_application_helper_patch.rbのようなファイルを作成し、以下のように記述します。
  2. init.rb内部で、redmine_sample_plugin_application_helper_patch.rbrequireします。

上に書いたように、一般的にはApplicationHelperを再定義せずに、追加したり置換したりしたいメソッドを別モジュールにまとめた上で、これをインクルードさせることが多いです。

View のフック関数からの呼び出し

Redmine のプラグインへのフレームワークとして、View の決められた場所に、プラグインのコードを埋め込む、という仕組みがあります。

ここでは説明しませんが、知りたい方はRedmine自体に手を入れずに見た目を変更する方法などをご覧ください。

いずれにせよ、Redmine::Hook::ViewListenerを継承したクラスを作成し、その中にあらかじめ定められた名前のフックメソッドを定義することで、View に任意のコードを追加できます。

View のフック関数から、追加したメソッドの呼び出し

上のSamplePluginApplicationHelperPatchのように定義した場合、新規に定義したメソッド (上の例ではsample_instance_method) は、View のフック関数から呼ぶことができません

ApplicationHelper 内のメソッドは、すべてのコントローラーや View から呼べるはずなのですが、呼ぶことができないのです。

呼び出せない理由

ApplicationHelperは、Redmine::Hook::ViewListener内で適切にincludeされています。
また、上で定義したサンプルでは、確かにSamplePluginApplicationHelperPatch::InstanceMethodApplicationHelperにインクルードされています。
そのため、間接的にSamplePluginApplicationHelperPatch::InstanceMethodRedmine::Hook::ViewListenerからインクルードされているように思えます。

しかし、実際にはそうなりません。

Redmine::Hook::ViewListenerApplicationHelperをインクルードするのは、プラグインの各ファイルがロードされるよりも早いタイミングで行われます。
そのため、Redmine::Hook::ViewListenerApplicationHelperをインクルードする頃には、まだSamplePluginApplicationHelperPatch::InstanceMethodApplicationHelperにインクルードされていません。

Ruby では、includeしたタイミングで継承関係が決定します。

例を挙げると、以下のようになります。

上の例で、B.ancestorsCが含まれてもよさそうですが、含まれません。

これは奇妙なように思いますが、 Ruby の仕様であり、どうしようもありません。

余談ですが、 Ruby 作者のまつもとさんも、これは奇妙に思っているらしく、Matzにっきの Traits の項目に、今後mixを追加しようと思っている旨が書かれています。

そのため、もともとの Redmine の ViewListener に関しても同様に、Redmine::Hook::ViewListenerApplicationHelperをインクルードするタイミングでは、まだSamplePluginApplicationHelperPatch::InstanceMethodApplicationHelperにインクルードされていないので、結果としてRedmine::Hook::ViewListenerの ancestors にはSamplePluginApplicationHelperPatch::InstanceMethodが含まれないことになります。

回避策

これは、例えば以下のようにすれば回避することができます。

このようにすることで、sample_instance_method自体はApplicationHelperに直接追加されるので、Redmine::Hook::ViewListenerからでも呼び出すことが可能になります。

もちろん、base.class_eval doの内部で、直接sample_instance_methodを定義しても呼び出すことができますが、このあたりはコードの見易さの問題で、好みの分かれるところだろうと思います。

2011年11月27日日曜日

NPAPI プラグインの作り方 for Windows その3

NPAPI プラグインの作り方 for Windows その3

続きです。

今回は、サンプルをビルドできるようにします。

ファイルの準備

前回の説明の中で書いたように、Gecko SDK 1.9.2と、バージョン 1.9.2 向けのnpruntime のサンプルをダウンロードします。

Gecko SDK は、展開して得られるincludeディレクトリ以下のみを使います。

面倒な人向けに、GitHubにまとめておいたので、そちらのoriginal_filesディレクトリ以下をお使いください。

以降では、これらをC:\work\test\includeおよびC:\work\test\npruntime以下に展開したとして話を進めます。

Visual Studio プロジェクトの作成

続いて、Visual Studio のプロジェクトを作成します。

Compiling The npruntime Sample Plugin in Visual Studioのページを参考に進めます。

プロジェクトの新規作成

以下のような設定で、新しいプロジェクトを作成します。

  1. メニューから「ファイル」-「新しいプロジェクト」を選びます。
  2. 種類は「Win32」の「Win32プロジェクト」を選びます。
  3. 「npsample」などの適当な名前をつけて、プロジェクトを作成します。
  4. ウィザード中の「アプリケーションの設定」で「DLL」を選び、「空のプロジェクト」にチェックを入れます。

ファイルの追加

サンプルファイルをプロジェクトに追加します。

ダウンロードしてあるC:\work\test\npruntime以下のファイルを、作成したプロジェクトのディレクトリにコピーしておくとよいでしょう。

  1. ソリューションエクスプローラーの「ソースファイル」を右クリックし、「追加」-「既存の項目」をクリックします。表示されたダイアログで、np_entry.cpp, npn_gate.cpp, npp_gate.cpp, plugin.cppを追加します。
  2. 同様に「ヘッダファイル」を右クリックし、plugin.h, resource.hを追加します。
  3. さらに「リソースファイル」を右クリックし、nprt.rcを追加します。

これで、図のような状態になっているはずです。

プロジェクトの設定

続いて、プロジェクトの設定をします。

  1. メニュー「プロジェクト」から「XXX のプロパティ」(XXX はプロジェクト名) をクリックし、プロパティを表示します。
  2. 左上の「構成」で Release を選択します。
  3. 「構成プロパティ」の「全般」の「ターゲット名」を、作成するプラグインの名前に設定します。これは、プロジェクト名と異なる名前を付ける場合には必須です。NPAPI プラグインの名前は、np*.dllという形式であるのが正しいようなので、プロジェクト名がそうなっていない場合は、ここで名前を設定します。
  4. 「構成プロパティ」の「全般」の「文字セット」を「マルチバイト文字セットを使用する」に設定します。
  5. 「構成プロパティ」の「C/C++」の「追加のインクルードディレクトリ」に、ダウンロードしておいた Gecko SDK のインクルードディレクトリである「C:\work\test\include」を追加します。
  6. 「構成プロパティの」の「C/C++」の「プリプロセッサ」の「プリプロセッサ定義」に、以下をすべて改行で区切って追加します。

    WIN32, _WINDOWS, XP_WIN32, MOZILLA_STRICT_API, XPCOM_GLUE, XP_WIN, _X86_

  7. 「構成プロパティの」の「リンカー」の「入力」の「モジュール定義ファイル」をnprt.defに設定します。nprt.defのフルパスを設定してください。

ファイルの修正

ビルド対象を「Debug」から「Release」に変更し、ビルドしてみると、いくつか警告やエラーが出ます。ここでは、その対応をします。

  1. まず、コンパイルエラーの一つ目であるplugin.cppprintfをすべて削除します。

    手作業で消してもよいですが、エディタなどで「printf」を「//printf」に置換すると楽だと思います。

  2. 続いて、strdupは ISO 標準の_strdupにするべきだ、という警告が出ているので、これもstrdup_strdupに置換することで対応しておきます。
  3. また、strcpyはセキュアではない、という警告については、対応が面倒なので、「プリプロセッサ定義」に_CRT_SECURE_NO_WARNINGSを定義リストに加えることで消してしまうことにします。
  4. nprt.defの一行目のLIBRARYの右に書かれた単語NPRTは、このプラグインの名前です。この名前が「構成プロパティ」で設定したものと異なる場合、警告が出るので、こちらも「構成プロパティ」で設定したものと同じ名前に設定します。
  5. 最後に、nprt.rc#include "afxres.h"#include "windows.h"に変更します。

これで、ひとまずビルドはでき、DLL ファイルが生成されるようになったと思います。

ここまでで、ひとまずプラグインとなる DLL のビルドができました。

しかし、このままではソースコードのバグにより、うまく動作しません

次回は、正しく動作するように修正していきます。

2011年11月21日月曜日

NPAPI プラグインの作り方 for Windows その2

NPAPI プラグインの作り方 for Windows その2

前回の続きです。

詳しい説明に入る前に

最初にことわっておきますが、NPAPI はこれから廃れゆく運命にあるだろう技術です。

今後は、Google が強力に推進する Native Client や、それと絡んだ NPAPI の置き換え規格となる PPAPI が主流になっていくだろうと思います。

ただし、これらの技術はまだ Hot すぎるので、今後まだまだ仕様が変わっていくだろうと思います。(私もまだそれほどきちんと調べていません。)

そういった事情から、まだもう少しは既存の技術である NPAPI を使わざるを得ない状況が続くのではないかと思っています。

サンプルとして作成するもの

何か目的があった方が説明しやすいので、サンプルとして「ローカルディスクの空き容量を返す」プラグインを作成することにします。

こういったことが現在の Native Client などで可能なのかよく分かりませんが、ひとまず HTML5 の範疇だけでは実現が不可能そうなことをやってみます。

NPAPI プラグインを作成するために必要なもの

以下のものが必要です。

開発環境: Visual C++ 2010 Express Edition

Visual C++ 2010 Express Editionが無料で入手できるのでよいと思います。試してはいませんが、VC++ 2005, 2008 でも問題なくコンパイルできると思います。

Gecko SDK 1.9.2

NPAPI プラグインを作成するには、Gecko SDK に含まれている include ファイルが必要です。 今回は、Gecko SDK 1.9.2を用いることにします。

npruntime のサンプル

今回作成するのは、NPAPI の中でも、WEB ページの JavaScript からプラグインのメソッドを呼ぶことができる NPRuntime という仕様に従ったものです。 スクラッチから書くのは大変なので、このサンプルコードをベースに進めることにします。 偉そうに書いていますが、私自身はそれほどきちんと NPAPI や NPRuntime の詳細を分かっていません。

ちなみに、私はnpruntime のサンプルのページにあるファイルをまとめてダウンロードする方法がよく分からなかったので、一つ一つ選んで、開いたページに出てくる "Raw File" のリンクからダウンロードしました。

これで、作成の準備は整いました。

次回は、サンプルの修正とビルドおよびテストをしてみます。

続く。

2011年11月16日水曜日

NPAPI プラグインの作り方 for Windows その1

NPAPI プラグインの作り方 for Windows その1

今回から何回かにわたって、NPAPI プラグインの作り方についてまとめておこうと思います。

ただし、私がまだ細かいところまで理解できているわけではないので、間違いなどがあるかもしれません。ご了承ください。

また、あくまで Windows 版の NPAPI についての記述です。

NPAPI プラグインとは

NPAPI とは、Wikipedia の解説ページにもあるように、Netscape Plugin Application Programming Interfaceの略です。

よく Firefox のアドオンや、Google Chrome の拡張機能と混同されることがありますが、ここで触れる NPAPI プラグインは、これらとは異なります。

例えば FLASH 動画を表示し再生するための Adobe Flash Plugin や、PDF ファイルをブラウザ内で表示できるようにする Adobe Reader Plugin などのことです。

NPAPI とは、このプラグインの仕様を定めたものであり、プラグインを開発するには、ある程度は NPAPI の仕様を知る必要があります。

NPAPI プラグインでできること

基本的に、NPAPI プラグインでは何でもできます。

そのため、Google Chrome の拡張機能では、通常は実現不可能な「ローカルファイルを検索する」や「ローカルのプログラムを直接起動させる」などを行うことも可能となります。

また、ブラウザ上に表示を行うタイプのものでは、Windows の場合はウィンドウのハンドルを取得できるので、完全に自由に描画を行うこともできます。

続く。

2011年11月15日火曜日

requireとrequire_dependencyとRuby 1.8.7とRuby 1.9.2

requirerequire_dependencyと Ruby 1.8.7 と Ruby 1.9.2

先日、Redmineの Version 1.2.2 もリリースされたので、プラグインを書くときに気になった、requirerequire_dependencyの違いおよびRuby のバージョンによるrequireの挙動の違いについて書こうと思います。

とは言っても、よく書かれている「カレントディレクトリがロードパスに含まれなくなった」ということではありません。

なお、これは Rails 2.3.11, 3.1.0 を見て書いています。また、Redmine は Version 1.2.2 を見ながら書いています。

requirerequire_dependency

Rails のコードを見たことがある人は分かると思いますが、Rails のコードではrequireの代わりにrequire_dependencyが使われていることに気付くと思います。

それぞれ簡単にまとめると以下のようになります。

require
Ruby の組込み関数。正確には、Kernelモジュールのメソッド。
引数で渡された .rb ファイルなどを読み込む。ただし、既に読み込み済みのファイルは読み込まない。
読み込まれたモジュールは$", $LOADED_FEATURESに格納されて管理される。
require_dependency
Rails が定義した独自のメソッド。activesupport/lib/active_support/dependencies.rbで定義されている。
引数で渡された .rb ファイルなどを読み込む。ただし、既に読み込み済みのファイルは読み込まない。development モードで実行している場合はloadを使って読み込まれる。production モードで実行している場合は、requireを使って読み込まれる。
これは、development モードで実行している場合は、動的にファイルを何度も読み込む必要があるため。

Redmine プラグインで注意すること

結論から書くと、Redmine プラグインの中では、Redmine のモデル、ビュー、コントローラー、ヘルパーを読み込むには、require_dependencyを使うべしということです。

ここで、requireを使うと失敗します。

上の説明で書いたように、production モードで実行するならどちらでも結局requireが呼ばれるので変わらない、と思うかもしれませんが、そうではありません。

以下で順に説明します。

Ruby 1.8.7 と Ruby 1.9.2 でのrequireの挙動の違い

まず、requireの挙動をもう少し詳しく見てみます。

Ruby 1.8.7 でのrequireの挙動

main.rbから、required.rbを、下記のようにrequireする場合を考えます。

ここでmain.rbを実行すると、出力は下記のようになり、二回目のrequireではrequired.rbがロードされていないことが分かります。

これは当然の挙動だと思います。

次に、以下のように、相対パスや絶対パスを指定してrequired.rbを読み込んでみます。

出力は以下のようになります。

このように、予想に反して (かどうかは分かりませんが) 三回とも読み込まれています。

Ruby 1.9.2 でのrequireの挙動

一方で、同じコードを Ruby 1.9.2 で実行してみます。

ただし、Ruby 1.9.2 では、カレントディレクトリがロードパスに含まれなくなっているので、先頭で追加してやった状態で実行してみます。

出力は以下のようになります。

今回は、一度しか読み込まれませんでした。

このように、Ruby のバージョンによってrequireの挙動は微妙に異なります。

Ruby 自体のソースを見れば分かりますが、「既に読み込まれたか」を表すリストである$", $LOADED_FEATURESに記憶する方法が、Ruby 1.8.7 と Ruby 1.9.2 では異なるようです。

Ruby 1.8.7 では指定されたファイルをそのままの形で$LOADED_FEATURESに覚えておきます。 そのため、require("required")require("./required")など、指定方法が異なると、複数回ロードされることとなります。

一方で、Ruby 1.9.2 では、常に絶対パスに直して覚えるようになっています。 その結果、実際に同じファイルであれば、指定方法が異なっても複数回ロードされることはありません。

Redmine プラグインでrequire_dependencyを使わないと正しく動作しない理由

以上を踏まえて、なぜ production モードであっても Redmine プラグインでrequire_dependencyを使わないとうまく動作しないのか、について書きます。

Redmine は、現在も Ruby 1.8.7 で動作しています。

そのため、あるファイルをrequireする際に、絶対パスで指定した箇所とファイル名だけを指定した箇所がある場合、そのファイルは二重にロードされることになります。

一方で、require_dependencyでは、コードを追うと分かりますが、最終的に引数のファイル名を絶対パスに展開してからrequireもしくはloadします。

ここで、以下のように書いてしまった場合を考えてみます。

この例では、ApplicationHelperモジュールのformat_activity_titleメソッドを上書きしています。

しかし、このファイルとは別のファイルでrequire_dependency("application_helper")と書かれていると、そちらではapplication_helperが絶対パスに展開されてから、requireに渡されることになります。

そのため、ファイル名だけを指定してrequire("application_helper")としたときとは別のファイルとして扱われてしまい、別途、application_helper.rbがロードされることになります。

その結果、せっかく上書きしたメソッドが再度上書きされてしまい、変更が反映されなかったように見えてしまいます。

というわけで、素直にrequire_dependencyを使うようにするのがよいと思います。

2011年11月12日土曜日

C++11 に合った書き方 その2

C++11 に合った書き方 その2

昨日の続きです。C++11 らしい書き方のページについて、気になるものを中心に書いていきます。

C++11 らしい書き方とは

スマートポインタを使え

スマートポインタを積極的に使え、ということのようです。

これまでにも、auto_ptrがありました

しかし、このauto_ptrは非推奨となり、代わりにunique_ptrを使えとのことらしいです。

私は、関数内部でnewしたインスタンスをauto_ptrにくるんで返り値として返す、ぐらいしかauto_ptrを使っていませんでしたが、そういった場合は、auto_ptrのムーブセマンティック考慮版とも言えるunique_ptrの出番のようです。

その他には、既に多くの処理系で実装されているshared_ptrなどが標準として入るようになりました。

これまでにも使っていましたが、仕事で使う際に、「仕様に入っているから」と堂々と使えるようになるのはありがたいことです。

nullptr

これは、単純に0でもNULLでもなく、新予約語のnullptrを使いましょう、ということです。

C++ では、C言語と異なり、void *を任意の型のポインタに代入することができません。例えば、以下のようにNULLを定義していると、C++ ではコンパイルエラーとなります。

そのため、基本的にNULLは、単なる 0 と定義されています。

C++ では 0 は特殊な扱いをうけており、何も指さないポインタの値として、任意の型のポインタに代入することができると定められています。

一方で、0 は整数値なので、NULLを 0 と定義した場合、以下のようなオーバーロードされた関数がある場合に、多くのプログラマの直感と異なる挙動を示してしまいます。

このように、直感と異なり (1) が呼ばれてしまいます。

C++11 では、このような事態を避けるために、nullptrという新たな予約語を定義し、何も指さないポインタの値として使え、かつ整数として扱われないものが導入されました。

Uniform Initialization and Initializer Lists

ここに例として挙がっているものと同様な下記コードで、私も以前にとても困ったことを覚えています。

最初は (1) のようなコードを書いており、なぜコンパイルエラーになるのかがまったく分からず、ふとしたはずみで (2) のように書くとコンパイルが通ったことを覚えています。

C++11 では、{, }による初期化が可能になるので、これらを使うことで、上の例のような場合を避けることができるようです。

また時間があれば、しっかりと C++11 の勉強もしようと思います。