#author("2023-11-11T07:42:44+09:00","default:post","post") #contents *この記事について [#d1b5d013] この記事は、[[SAORIの仕様書>http://www.boreas.dti.ne.jp/~sdn/saori.html]]にあるSAORI-universalの説明を、里々で使用するSAORIという前提で、できるだけ噛み砕いて説明したつもりです。 わかりにくければ、ぜひかきなおしてください。 *必要スキル [#ifa058e8] C++ で ダイナミック リンク ライブラリ(dll)をビルドできる方。 *前準備 [#jc4171f0] GlobalAlloc() や GlobalFree() などのWinAPI関数を使う必要があるので、 その宣言を含む、Windows.hをインクルードしておきましょう。 *実装が必要な関数 [#e681a375] SAORI-universal の作成には、load(),unload(),request()の3つの関数の実装が必要です。 load() と unload() の2つは、両方実装する・両方実装しないが任意です。(片方だけはだめ) load() と unload() を実装する場合、SAORIの仕様上、ゴーストの読み込み時から終了時まで(里々の読み込み時から解放時まで)SAORIが読み込まれていることになりますが、実装しない場合は、いつ読み込みや解放を行っても良いことになっています。そのため、ゴースト起動中に保持しておきたい情報が有るのであれば、実装するようにしておきましょう。 **load() [#f5d82bbf] SAORIが読み込まれる際に一番最初に呼び出される関数です。 (DllMainなどのDLL エントリポイント関数を実装しているのであれば、そちらが先に呼びだされます。) 宣言: extern "C" __declspec(dllexport) BOOL __cdecl load(HGLOBAL h, long len); 説明: SAORIが呼び出した際に呼び出されます。 SAORIはここで初期化を行うのが望ましいようです。 引数: HGLOBAL h SAORIのあるフォルダの絶対パスが\で終端された文字列へのポインタ。 SAORIはこれを利用して、同フォルダ以下にあるファイルを読み書きするようにしてください。 (このHGLOBALは、char* として扱うことが出来ます。) このポインタはSAORI側で、GlobalFree() を使って解放する必要があります。 long len h のバイトサイズ。 返り値: 関数のの成否。 FALSEが返った場合、SAORIのロードに失敗したと判断することができます。 注意: h をGlobalFree() で開放しない場合、メモリリークが発生します。 **unload() [#df4b2fb5] SAORIが解放される際に最後に呼び出される関数です。 宣言: extern "C" __declspec(dllexport) BOOL __cdecl unload(); 説明: SAORIのアンロード時に呼び出されます。 (里々ではゴーストの終了時、里々のアンロードのタイミングで。) 返り値: 関数の成否。 **request() [#l9e6a2fd] load() の後と、SAORIの呼び出し時に呼びだされます。 宣言: extern "C" __declspec(dllexport) HGLOBAL __cdecl request(HGLOBAL h, long *len); 説明: load() と、SAORIの呼び出し時に呼び出されます。 引数: HGLOBAL h リクエスト文字列へのポインタ。 (load() 同様に、char* として扱うことが出来ます。) このポインタはSAORI側で、GlobalFree() を使って解放する必要があります。 long *len リクエスト文字列のバイトサイズを格納する変数へのポインタ。 関数終了時には、返り値の HGLOBAL のバイトサイズを参照先に格納するして、里々に返り値の文字列のバイトサイズを通知します。 返り値: SAORIの実行結果文字列を格納したHGLOBAL。 返り値のHGLOBALを作成するには、GlobalAlloc() を使って確保した領域に返り値の文字列を格納します。返り値の文字列の書式は、下の項目に従ってください。 GlobalAlloc() の最初の引数(UINT uFlags)には GMEM_FIXED を指定します。また、この時に確保したバイトサイズを引数の len の参照先に設定します。 注意: h をGlobalFree() で開放しない場合、メモリリークが発生します。 バージョンチェック時の呼び出しは、load() がない場合にも発生するはずです。 *リクエスト文字列 [#ud160ec8] リクエスト文字列は次の書式に従った文字列で行われます。 (最初の行(コマンド SAORI/1.0)と最後の行(\r\n)以外は順不同です。) (コマンド) SAORI/1.0\r\n SecurityLevel: (セキュリティレベル)\r\n Argument0: (引数0)\r\n Argument1: (引数1)\r\n Charset: Shift_JIS\r\n Sender: SATORI\r\n \r\n **コマンド [#a4a646d4] GET Version EXECUTE このどちらかが入ります。 ***GET Version [#c0aed675] GET Versionは、load()が呼び出された後(loadがない場合でも呼び出されるはず)に呼び出されます。 これは、DLLがSAORIであるかを確認しています。(もっと言えば期待するバージョンのSAORIか、なのですが今のところ SAORI/1.0 しか無いです。) GET Versionには、 SAORI/1.0 200 OK\r\n\r\n とだけ返しておけば問題ありません。 また、そうでなければSAORIとして認識されません。 ***EXECUTE [#xc72f38d] こちらは実際にSAORIを呼び出した際に呼び出されます。 Argumentをもとに処理を行い、結果を返すと良いでしょう。 **セキュリティレベル [#gf62f098] Loca| External このどちらかが入ります。 里々の場合、通常はLocalが、外部からスクリプトが入力されて実行される場合はExternalが入るようです。 一応里々自体は、SSPの開発者モードを有効にした上で、里々で $デバッグ=有効 にしないと外部からスクリプトを入力できないはず(ShioriEchoが使えない)なので、気にする必要は無いでしょう。 **引数 [#re6aad83] さおりを呼び出した時に使われた引数です。 Argument0から数字は1ずつ増えていきます。 **その他 [#kb529c6b] CharsetとSenderは、それぞれ里々の場合はこのような固定値になりますが、他のSHIORIでは他の値になることがあります。(少なくともSenderはSHIORIに名前になるでしょう。) Charsetが異なる場合は、この文字列の文字コードも異なることになりますので、注意が必要です。 逆に、里々専用のSAORIにするのであれば、考慮の必要は無いでしょう。 *返り値の文字列 [#c97aac33] 返り値の文字列も次の書式に従った文字列で行います。 SAORI/1.0 (ステータスコード)\r\n Result: (返り値)\r\n Value0: (追加の返り値2)\r\n Value1: (追加の返り値1)\r\n Charset: Shift_JIS\r\n \r\n **ステータスコード [#zf618f1d] SAORIの実行が成功したかどうかを返します。 いくつか決まった種類があるので、合うものを使います。 200 OK 正しく実行が完了した。 204 No Content 正しく呼び出しが実行されたが、返り値は無い。 400 Bad Request 呼び出しの不備。リクエスト文字列が正しくない場合や、引数が期待するものでなかった場合のエラーを示します。 500 Internal Server Error SAORIでエラーが発生し、正常に処理が完了できなかった場合に、これを返します。 **返り値 [#w1e9af72] SAORIの返り値です。 例えばssuのsplitは、分割数を返しますが、それがこれに当たります。 **追加の返り値 [#i16cb133] 返り値が複数ある場合、Value0から1ずつ増える形で複数の返り値を返せます。 SAORIの実行後、里々で(S0)などのように取得することが出来ます。 *全体的な注意 [#v286e4e5] SAORIでメモリ違反などの致命的なエラーが発生した場合やアサートを呼び出した場合は、SSPごと落ちる可能性があるので、エラーは必ずステータスコードとして返すようにし、関数自体は正常に終了するようにしてください。 *外部リンク [#h488ef29] - [[SAORI/1.0仕様書:http://www.boreas.dti.ne.jp/~sdn/saori.html]] //*外部リンク [#h488ef29] //- [[SAORI/1.0仕様書:http://www.boreas.dti.ne.jp/~sdn/saori.html]] //冒頭と同じリンクなので非表示