ZIDNEYさん作成の高性能付箋紙ツール/0.93系ペルソナウェア互換実行環境ZMemoは、0.93系ペルソナウェア(以下PWS0.93とします)のバイナリを実行できるだけでなく、θ(旧称DDE)言語という綾織上位互換のスクリプト言語*1を備えています。そしてこのθ(旧称DDE)言語で拡張された機能の大部分はPDK0.53のayacをうまく騙してやることでPWS0.93のバイナリの中に混在させることが可能ですので、PWS0.93で実行した場合はペルソナウェアの機能だけを使い、ZMemo上で実行した場合はZMemoで拡張された機能も使えるという両用バイナリ*2を作成することができます。
本稿はZMemo/PWS0.93両用バイナリを作成しようとしている方の一助となればと思って、拙作和泉・リアン・佳乃ぺるの作成経験を例として両用バイナリ作成の注意点について記述しています。なお、本稿では綾織0.93およびθ(旧称DDE)言語の言語仕様やプログラム書法については既に知識があるものと仮定して*3執筆しています。また、絢夏ベースのペルソナの場合をかなり想定した記述になっています(^^;。
*1 なおθ(旧称DDE)言語の言語仕様についてはこちらをご参照ください。
*2 もちろん、ZMemoで拡張された機能を0.93系ペルソナウェアで利用することはできないです(^^;また、あくまでayacを「騙してθ言語のコードをaya*.outに混在させるという両用バイナリの性格上、利用できる機能にはいくつかの制限があります。具体的な制限例についてはこちらを御覧ください。
*3 ZMemo拡張機能の実際の使用法については例えばDDE言語改めθ(theta)言語仕様書のページで公開されているサンプルスクリプトを参照してください。
ayacを騙してZMemoの拡張機能を含んだPWS0.93バイナリを作成するには大きく分けて二つの作業が必要です。
第一は、件のPWS0.93バイナリがZMemoの拡張機能を含んでいることをZMemo(正確にはそのスクリプトエンジンscriptdll)に知らせること、第二にayacでエンコードできるようにZMemo拡張機能で提供される関数を変数としてダミー定義することです。
第一の作業はZMemoで定義されているキーワード、EnableDDEReservedWordをソースコード中の任意の場所で定義することで行ないます。なお、このEnableDDEReservedWordは必ずaction定義部の外に記述する必要があり、またローカル変数を用いての初期化は不可である点注意してください。ソースの見易さ、美しさという点では変数の初期化を担当しているソースファイルの先頭部分で定義するのが良いかと思います。例えば拙作佳乃ぺるの場合はpersona.ayaの先頭で
int EnableDDEReservedWord = 11; //ZMemo用機能拡張有効化
int Zmemo_ver = 11;
という形で定義されています。なお、ここで代入されている11という値は、ZMemo(正確にはscript.dll)の機能レベルを表しています(これについては後述します)。
第二の作業については任意の場所(ただし実際にZMemo拡張機能を使用する部分よりは前)で、使用するZMemo拡張機能の関数をintもしくはstring変数としてダミー定義することで行ないます。この場合、戻り値にintを返す関数はint変数として、stringを返すものはstring変数として定義してください。void型の場合はどちらでもよいと思われます。拙作佳乃ぺるの場合、persona.ayaのaction PersonaInitialSet@( 0 )内で
/* for 1.05 */
string EditBox; //ZMemo用ダミー定義
string TextBox;
int FileExist;
/* for 1.06 */
string PostMessage;
string GetMessage;
int GetMessageFrom;
・
・
・
int Pop3_CheckMail;
int GetHeadlineStatus;
string GetHeadlineScript;
string GetHeadlineScriptEntry;
string TalkText;
という形で定義していますが、これについてはZIDNEYさんのDDE言語改めθ(theta)言語仕様書のページからダウンロードできる、DDEextension.ayaを適切な場所で#importしたほうがスマートでしょう(^^;。なお、複数のモジュール(*.out)に分かれるペルソナで複数のモジュールでZMemo拡張機能を呼び出す場合は、変数を呼び出すときのようにextern指定してください。
両用バイナリを作成する上でもう一つ不可欠な作業は現在バイナリが実行されている環境がPWS0.93であるのか、ZMemoであるのかを判定することです。幸いにもこの判定はPWS0.93/ZMemoが提供している標準プロパティ、Pws_Verを参照することで簡単に行なえます。ZMemoではこのプロパティには必ず0.93 semicompatible(^^; が返されることになっていますので、
Pws_version = GetProperty ("Pws_Ver");
if (StrSearch(Pws_version,"compatible") != 1 ) { //PWS0.93系の場合
IsZmemo = 0;
EnableDDEReservedWord=0;
Zmemo_ver = 0;}
のようなコードを書くことによってPWS0.93と判別できます。
以上の作業でとりあえずZMemo/PWS0.93共用バイナリを作成することは可能ですが、しかしこのままの状態で作成された共用バイナリはPWS0.93上の動作は問題なくてもZMemo上で動作させた場合、ある環境では動くのにある環境ではエラーをだすといった不具合を生じる可能性があります。なぜならZMemo(Script.dll)のバージョンによってサポートされている拡張機能には差異があり、より上位バージョン用の機能を含んだ共用バイナリは下位バージョンのscript.dllでは正常に動作しない場合があるからです。
そこで、ZMemo/PWS共用バイナリを作成する場合は、ターゲットとなるScript.dllのバージョンを明示するとともに、それより低いバージョンのScript.dllで動作させた場合でもエラーを出さないようにする配慮が求められます。そのために用いるのが、EnableDDEReservedWordの設定時に代入したZMemo機能レベルです。Script.dllの機能レベルは綾織0.93上位互換機能のサポートを開始したver.1.05を1として以後Script.dllのバージョンが上がるごとに1ずつ増加し、現在最新のver.1.15では11となっています。なお、script.dllのバージョンは標準プロパティDDE_Verにバージョン番号(ex. 1.10)、リビジョン番号(、β版バージョン、デバッグバージョン)の順にスペースで区切られて返されますので、後述のようなコードを書くことによって判定できます。
拙作ペルソナでは機能レベルの判定は実際にはZMemo/PWS0.93の判定と一体化した次のようなコードで行なっています。
action IsZmemoCheck@ ( 0 )
{
string Pws_version;
string DDE_version;
string DDE_ver_main;
string DDE_ver_rel;
Pws_version = GetProperty ("Pws_Ver");
DDE_version = GetProperty ("DDE_Ver");
DDE_ver_main = GetToken (DDE_version, " ",0);
DDE_ver_rel = GetToken (DDE_version, " ",1);
if (StrSearch(Pws_version,"compatible") != 1 ) {
IsZmemo = 0;
EnableDDEReservedWord=0;
Zmemo_ver = 0;}
else {
IsZmemo = 1;
Zmemo_ver =(atoi(GetToken(DDE_version,".",0))*100+atoi(GetToken(DDE_version,".",1))
-104);
/*
ここで104]を引いているのは、PWS0.93のコード実行エンジンが追加された
Script.dllのバージョンが1.05だからです。
*/
}
(略)
}
#なおこの部分のソースコードは、IsZmemo.lzhとして公開しております。
そして実際にZMemo/PWS0.93両用バイナリでZMemo独自拡張機能を呼び出す時に、
if (Zmemo_ver > 3) {
AddItem("ヘッドラインを取ってきてくれないか?")
{
Body_CG = "normal2";
Call( PersonaCG );
Talk( "うん、わかったよ〜。\n");
ReadHeadline();
}}
if (Zmemo_ver > 5) {
AddItem("メールをチェックしてくれないか?")
{
Body_CG = "normal2";
Call( PersonaCG );
Talk( "うん、わかったよ〜。\n");
Call(Getmail);
}}
この例のようにScript.dllの機能レベル判定の結果を利用して拡張機能を呼び出すことによって、該当機能未実装のバージョンのscript.dllでの呼び出しを防止し、エラー発生を回避することができます。
訂正:上記のZMemo機能レベルの判定とEnableDDEReservedWordを操作しての下位互換性維持のテクニックですが、残念ながら現行のバージョン(ver.1.13)の機能を取り込んだ両用バイナリは、ver.1.12以下のscript.dllでは動作させることが出来なくなっているようです。以前の(ここでサンプルにあげたScript.dll ver.1.09頃まで?)のバージョンでは上記のテクニックは有効だったはずなのですが、現在ではこのテクニックは無効のようですので、代わりに、要求するScript.dll よりも古いバージョンで実行された場合は、Script.dll ver.1.12r8から実装された要求バージョンチェック機構を利用して必須Script.dllバージョンを表示し、終了させるようにしてください。
さらに追記(^^; :
上記の問題ですが、script.dll 1.05以降で定義されているNoPreCompile変数を0以外で初期化することによって、script.dllの実行前の文法チェックは回避できますのでこれを指定することで下位互換性を維持することができると思われます。ただしこの場合重大なミスがあってもスクリプト起動時に文法チェックが行なわれないことによって、ZMemo本体、さらにはOSが落ちるなどの問題が発生する可能性がありますので、十分なバグチェックを行ない、拡張機能が非対応バージョンで呼ばれないことを確認するまではこの指定は行なわない方が良いでしょう。また、なぜかNoPreCompile =1を指定してもzmemo.exe 1.05r7とscript.dll1.12r10の環境では予想外のエラーが出て日和ぺるは正常に稼動しませんでした。
前述したように、ZMemo/PWS0.93の両用バイナリにはayacを騙すという実現方法に起因するいくつかの制限事項があります。すべてを把握しているわけではないですが、執筆者が知る限りでは以下のような制限があります。
ayacがfunctionというキーワードを通さないためにfunctionは使うことができません。actionに書き換えてください。例えばZMemo本来の書法で
int x= 1;
string aMesage="hoge";
if (foo(x,aMessage) ==0)
{・・・・・・}
function foo(int bar,string baz) {…… fooresult =0; return fooresult;} のようなコードは、
int Result;
int x =1;
string aMessage="hoge";
action foo@(0) { …… Result=0; }
Call (foo);
if (Result == 0) {・・・・・・} のような形に書き換える必要があります。
1.と同じくayacを通すことができません。ですのでdouble値を扱うSqrt()やcos()、sin()、PIなどの数学関数は使うことができません。小数の計算を行なう場合綾織0.93と同様のテクニックが必要になります。
式の記法は綾織の制限にしたがってください。
なぜか綾織のマニュアルには記述がないようですが 見落としです。PDKのマニュアルに記載がありました(^^; breakは通ります。
これもayacを通すことができないため、オブジェクト名.メソッド名の形式になっている機能は使用できません。関数版が用意されているものは関数版を使用してください。残念ながら関数版が用意されていないもの(Screen関連やSurface、Splite、Pic関連の機能など)は両用バイナリでは使用できません。
これはPWS1.x側の仕様に起因すると思われる制限です。1.02以下のバージョンではZMemo拡張部分がコンバータで引っかかるために、1.03以降では綾織0.93解釈エンジンの互換性に問題がある*1ためにZMemo/PWS0.93両用ペルはPWS1.xでは動作しません。まあ、あえてPWS0.93系ペルをPWS1.xで動作可能にする必要はないと思いますが、どうしても1.xでも動かしたい場合は両用ペルにすることは諦めなければなりません。もしかするとZMemo機能拡張を後述の手段を使ってすべて*.euu側に格納すれば1.x系でも動作する両用ペルを構築可能かもしれませんが執筆者は未検証です(検証する予定もありません)。
*1 これはプラエセンス側の故意・もしくは怠慢の結果で、こちらとしては対応不可能です...
ただし、Script.dll1.15r1以降ではaya**.outの後ろにより大きな数字のファイル名を持ったmio**.euuファイルを結合させることが可能になりましたので、前述の1〜5の制限は回避可能です。そのためには上記の制限に抵触する部分のコードをaction、もしくはfunctionで包んでaya*.out側のコードから呼べる形にし、DDKのeuue.exeを使ってeuu形式のファイルにします。例えばayac.exeでコンパイル可能なモジュールがaya00.out〜aya02.outとなっている場合、ayac.exeを通すことができないモジュールを例えばmio03.euuにエンコードしてaya00〜aya02と同じフォルダに置けばScript.dllにより自動的に結合されてmio03.euu内の機能がaya00〜02のコードから呼び出し可能になります。ただし、double、bool変数を用いる場合、その変数やそれを用いる関数の利用は*.euu側ののコードで完結させる必要があることに注意してください。
#なお、2001/12/8現在ではScript.dllの問題のため、aya**.outから呼び出す*.euu#この制限はScript.dll ver.1.15r6以降で修正されました。ただし安全のため、aya++.out
#モジュール内ではfunctionを使用することは出来ない、また綾織互換モードで構築さ
#れたDDEマスコットでは引数を持つfunctionを使用することができないようです。
#から呼び出す*.euuでのfunctionの使用は、Script.dll
ver.1.16以降に限ったほうが良いで
#しょう
Script.dll 1.16から関数定義の形式が変更され、以前の function 関数名 (引数、{引数…}) {}からC言語互換の定義(関数の戻り値型 関数名 (引数、{引数…}))となったのですが、Script.dll1.16r4現在この形式は両用バイナリ、および両用バイナリと同じソースからビルドしたZMemo専用バイナリでは使えないようです。function 関数名形式は問題なく使えますのでこちらを使ってください。将来のバージョンで新形式の関数定義が両用バイナリで使用できるようになるかどうかは現時点では不明です。
以上駆け足で、ごく大雑把にZMemo/PWS0.93両用バイナリ作成時の留意点を指摘し、実装例を提示してみました。この説明ではわかりにくい点、説明不足な点がまだいくつもあるとは思いますが、それらの問題点については掲示板やメール等でご指摘いただければ幸いです。この拙い文書がZMemo/PWS0.93両用ペルソナの作成にいささかなりとも役立つことができれば望外の幸せです。