NPAPI プラグインの作り方 for Windows その5
今回は、サンプルの簡単な説明をします。
なお、今回でひとまず NPAPI プラグインの作り方は終了です。要望があればコメント欄に書いてください。気が向けば対応します。
これまでと同じく今回のサンプルは、GitHub のページからダウンロードできます。
この中の modified_files/npruntime/plugin.cpp
が主に説明する対象です。
HTML ファイルからプラグインの呼び出し
テスト用の HTML ファイル test.html
からプラグインを参照しているのは以下の箇所です。
1 | < embed id = "embed1" type = "application/mozilla-npruntime-scriptable-plugin" width = 600 height = 40 > |
embed
タグはプラグインをロードするために使われるタグで、type
属性にプラグインの種類を指定することで、目的のプラグインをロードします。
type
属性には、プラグインの MIME type を指定します。
サンプルの場合は、application/mozilla-npruntime-scriptable-plugin
になっています。
このプラグインの MIME type は、Windows の場合はリソースファイル nprt.rc
内で定義されています。
オリジナルのプラグインを作成する場合は、以下の MIMEType
の箇所を好きなものに変更してください。
もちろん、変更した場合は test.html
の embed
タグの方も同じ文字列に変更してください。
なお、MIME type の仕様としては、独自の種類を定義する際は application/x-
で始まる文字列にすることが推奨されているようです。
1 2 3 4 5 6 7 | VALUE "LegalTrademarks", "\0" VALUE "MIMEType", "application/mozilla-npruntime-scriptable-plugin\0" VALUE "OriginalFilename", "nprt.dll\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "npruntime scriptable example plugin\0" VALUE "ProductVersion", "1, 0, 0, 1\0" VALUE "SpecialBuild", "\0" |
複数の MIME type に対応する場合は、|
で区切ればよいようです。
なお、HTML ファイルからの呼び出しには直接は関係ありませんが、ProductName
でプラグインの名前を、FileDescription
でプラグインの説明を記述するなどが決められています。
また余談ですが、Windows 向けの NPAPI ではこのようにリソースファイルで MIME type を指定しますが、Unix などでは、np_entry.cpp
内の NP_GetMIMEDescription
関数で返す文字列で MIME type を指定します。
逆に言えば、Windows 環境では NP_GetMIMEDescription
で返される文字列の値は、ブラウザに参照されることはありません。
プラグイン内で使われる変数
NPVariant
NPAPI プラグインの中では、JavaScript 側とやりとりをする際に、NPVariant
という型がよく出てきます。
この型は、npruntime.h
内で以下のように定義されています。
1 2 3 4 5 6 7 8 9 10 11 | // npruntime.h より typedef struct _NPVariant { NPVariantType type; union { bool boolValue; int32_t intValue; double doubleValue; NPString stringValue; NPObject *objectValue; } value; } NPVariant; |
type
フィールドには、現在 value
に保持している値の種類が格納されており、value
にその値が保持されます。
Windows の COM を扱う際に登場する VARIANT
型と同様なものだと考えればよいと思います。
NPVariant
型の変数に値を代入したり値を読み出したりする際は、BOOLEAN_TO_NPVARIANT
や NPVARIANT_TO_BOOLEAN
マクロを使うとよいと思います。
変数の動的確保と解放
JavaScript 側とやりとりをする場合、特に文字列などをメソッドなどの戻り値として返す場合、その文字列の領域を動的にメモリ上に確保する必要があります。
通常の C 言語であれば、malloc
と free
などを呼び出しますが、NPAPI プラグインでは NPN_MemAlloc
と NPN_MemFree
を用いることになります。
ちなみに、前回の記事でサンプルの修正をしたのは以下の部分でした。
1 2 3 4 5 | - STRINGZ_TO_NPVARIANT(_strdup("foo return val"), *result); + const NPUTF8 *ret_str = "foo return val"; + NPUTF8 *s = (NPUTF8 *)NPN_MemAlloc(strlen(ret_str) + 1); + strcpy(s, ret_str); + STRINGZ_TO_NPVARIANT(s, *result); |
これは、本来であれば NPN_MemAlloc
でメモリを確保すべきなのですが、_strdup
で文字列用のメモリを確保してしまっていることが問題でした。
NPN_MemAlloc
, NPN_MemFree
などは、プラグインロード時にブラウザから関数ポインタとして渡されます。
そのため、プラグイン作成者側には、その内部でどのようにメモリが管理されているかを仮定することはできません。
そのため、NPN_MemAlloc
内部で malloc
と同様のものを呼んでいるのであれば、サンプルを修正しなくても動作するかもしれませんが、そうでない場合は、ブラウザ側が戻り値の文字列を NPN_MemFree
しようとしたタイミングでクラッシュすることになります。
プラグインのメソッドの呼び出し
ここでは、プラグインのメソッドの呼び出し方法と、新しいメソッドの追加方法について書きます。
プラグインのメソッドの呼び方
プラグインのメソッドを呼ぶには、以下のように embed
要素を取得し、その要素に対してメソッドを呼び出します。
1 2 3 4 | // <embed id="embed1" type="application/mozilla-npruntime-scriptable-plugin" width=600 height=40> の要素を取得 var elem = document.getElementById( "embed1" ); // 例えば foo 関数を呼ぶ。 elem.foo(); |
プラグインのメソッドの定義
呼び出されるメソッドは、以下のように定義されます。
ここでは、実際にサンプルで定義されている foo
メソッドについて説明します。
HasMethod
の対応
まず、HasMethod
関数の対応をします。
この関数は、特定の名前のメソッドを持っているかどうかを判定するための関数です。
サンプル内では、下記のようになっている箇所です。
1 2 3 4 5 | bool ScriptablePluginObject::HasMethod(NPIdentifier name) { return (name == sFoo_id || name == sDiskSize_id); } |
ここで、sFoo_id
は、CPlugin::CPlugin
で初期化されていますが、"foo"
という文字列を表すものだと思ってください。
上の例では、sFoo_id
が表す foo
という名前か、sDiskSize_id
が表す diskSize
という名前の文字列なら true
を返すことになります。
メソッドの実体の定義
メソッドの実体は、以下のような関数内で定義されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | bool ScriptablePluginObject::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result) { if (name == sFoo_id) { // printf ("foo called!\n"); NPVariant docv; NPN_GetProperty(mNpp, sWindowObj, sDocument_id, &docv); NPObject *doc = NPVARIANT_TO_OBJECT(docv); // ... return true ; } else if (name == sDiskSize_id){ // 簡単なエラーチェック if (argCount != 1 || !NPVARIANT_IS_STRING(args[0])){ BOOLEAN_TO_NPVARIANT( false , *result); return true ; } NPString drive = NPVARIANT_TO_STRING(args[0]); ULARGE_INTEGER free_size, total_size; // 日本語などもサポートするなら MultiByteToWideChar などを使う。 std::string d(drive.UTF8Characters, drive.UTF8Length); if (!GetDiskFreeSpaceEx(d.c_str(), &free_size, &total_size, NULL)){ BOOLEAN_TO_NPVARIANT( false , *result); return true ; } INT32_TO_NPVARIANT((int32_t)(total_size.QuadPart/(1024*1024)), *result); return true ; } return false ; } |
この Invoke
関数は、プラグインに対してメソッドが呼ばれたときに、メソッド名や引数の配列などとともに呼び出されます。
そこで、この関数の中で、メソッド名に応じて処理を分けてやることで、各メソッドの処理を実装することができます。
上の例では、もともと存在した foo
メソッドに加え、新規に diskSize
というメソッドを追加した場合です。
このように、いくらでも自由にメソッドを追加することができます。
もちろん、数が多くなればメソッド名とコールバックをハッシュに格納するなどして、効率化すべきでしょう。
プロパティの場合も、HasProperty
, GetProperty
, SetProperty
関数の内部で同様に実現されています。
ひとまずこれで簡単な NPAPI プラグインの作成方法の説明を終わります。
質問などがあれば、コメント欄までお願いします。
0 件のコメント:
コメントを投稿