高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technical Note TN1164
Native Scripting Additions

目次

このテクニカルノートでは、Mac OS 8.6で導入されたAppleScriptのスクリプティング機能追加のメカニズムと、Mac OS X用のスクリプティング機能追加を作成するための拡張APIについて説明します。

このテクニカルノートは、スクリプティング機能追加を開発するアプリケーションデベロッパ向けに書かれています。

[2001年9月13日]






スクリプティング機能追加とは何か?

スクリプティング機能追加はAppleScript言語の構文を拡張するメカニズムを提供します。スクリプティング機能追加はAppleイベントを処理するものと、Appleイベントデータのcoercionを行うものに分類できます。Appleイベントを処理するタイプのスクリプティング機能追加は、アプリケーションで使われているAppleイベントハンドラと同じ要領で実装しますが、スクリプティング機能追加は特殊な実行環境におかれているので、Appleイベントハンドラや環境変数を正しく扱うためには、更に次のルーチンを実装しなければなりません。

  • イベントハンドラやcoercionハンドラインストール、メモリの確保、グローバル変数の準備など、実行に必要な初期化作業

  • スクリプティング機能拡張ロード/アンロードのタイミングを決定するリファレンスカウントの徹底

  • 各種ハンドラの削除、作業領域の解除などの終了作業

スクリプティング機能追加によってインストールされるAppleイベントハンドラとAppleイベントデータcoercionハンドラは、一般的にアプリケーションで使われるハンドラと同じ方法で実装します。スクリプティング機能追加とアプリケーションの違いは、コードのパッケージされている方法と、実行方法です。これから、スクリプティング機能追加のパッケージと実行方法について詳しく説明します。



スクリプティング機能追加のパッケージ化

スクリプティング機能追加は2通りのパッケージが存在します。

  1. Mach-O形式のMac OS Xバンドル

    Mach-O形式のスクリプティング機能追加はMac OS Xバンドルとしてパッケージ化します。Mach-O形式のスクリプティング機能追加次のいずれかの方法で特定されます。
    1. バンドルのCFBundleSignatureが“osax”である
    2. バンドル名の拡張子が“.osax”である


  2. CFM形式のCarbonLibおよびInterfaceLibにリンクされたシングルバイナリ

    CarbonLibにリンクされたスクリプティング機能追加のみがMac OS Xで使用できます。CFM形式のスクリプティング機能追加はファイルタイプとクリエータコードで特定されます。スクリプティング機能追加はファイルタイプが‘osax’、クリエータコードは汎用的な値(‘ascr’)または‘BNDL’リソースで定義されている値であっても構いません。

    註:
    InterfaceLibにリンクされたスクリプティング機能追加はMac OS Xで使用できません。CarbonLibにリンクされたスクリプティング機能追加はMac OS 9で安全に使用できません。



先頭に戻る



スクリプティング機能追加の初期化

スクリプティング機能追加の初期化ルーチンでは、必要なハンドラのインストール、メモリの確保、その他の準備処理を行います。初期化ルーチンは以下のように構成します。

  1. Gestalt、sysctlなどを使って、実行に必要なシステムリソースの存在を確認します。ここではメモリの確保や実行に必要なファイルの確認も行います。

  2. AppleイベントハンドラやCoercionハンドラをインストールします。スクリプティング機能追加のハンドラはシステムディスパッチテーブルにインストールする必要があります。リスト1ではシステムディスパッチテーブルにハンドラをインストールする方法を紹介しています。

    リスト1 システムディスパッチテーブルにハンドラをインストールする

    
         Boolean isSysHandler = true;
    
         anErr = AEInstallEventHandler( theAEEventClass, theAEEventID,
                                 theHandlerUPP, refcon, isSysHandler);
                                 
         anErr = AEInstallCoercionHandler( fromType, toType, theHandlerUPP,
                                 refcon, fromTypeIsDesc, isSysHandler);
    
    


    重要:
    初期化ルーチンでnoErr以外の戻り値を返す場合は、ハンドラをインストールしないことが肝要です。既にインストールされてしまっている場合はハンドラを外します。



  3. 定数の初期化や初期化ルーチンパラメータの保存など、その他の初期設定を行います。

スクリプティング機能追加の初期化ルーチンはパッケージ方法によって、実装方法が異なります。CFM形式の場合、Code Fragmentの初期化ルーチンが使用されます。一方、Mach-O形式の場合、“SAInitialize”と言うルーチンを定義して、エキスポートしなければなりません。

CFM形式のスクリプティング機能追加の場合は、Code Fragmentの初期化ルーチンが呼ばれます。スクリプティング機能追加は、初期化ルーチン以外のところで自分のリソースフォークを開くことがあれば、初期化ルーチン内で取得したリソースファイルへの参照をグローバル変数などに保存します。リスト2はCFM形式のスクリプティング機能追加の初期化ルーチンの例です。



リスト2 CFM形式のスクリプティング機能追加の初期化ルーチン


OSErr CFMSAInitialize(InitBlockPtr initBlkPtr) {
    OSErr err;

    ...ここで初期化を行う...
    
    return err;
}



スクリプティング機能追加の初期化ルーチンに渡されるinitBlkPtrパラメータは、スクリプティング機能追加のリソースファイルを参照するための情報が含まれています。初期化ルーチン以外のハンドラで自分のリソースフォークを開く場合は、参照情報をグローバル変数などに保存しておきます。リスト3ではエイリアスをグローバル変数に保存しています。



リスト3 CFM形式のスクリプティング機能追加の初期化ルーチン


static AliasHandle gMyAdditionLocation;

OSErr CFMSAInitialize( InitBlockPtr initBlkPtr ) {
    OSErr err;

    /* このルーチン以外のところで自分のリソースフォークを開く
       ことがあれば、グローバル変数にスクリプティング機能追加
       ファイルへの参照を保存します。 */

    err = NewAlias( NULL, initBlkPtr->fragLocator.u.onDisk.fileSpec,
                    &gMyAdditionLocation);
    if( err == noErr ) ...
    
    return err;
}



Mach-O形式のスクリプティング機能追加は“SAInitialize”と言う関数をエキスポートしなければなりません。この関数はスクリプティング機能追加初期化時に呼ばれます。リスト4ではMach-Oの初期化関数の一例を示します。



リスト4 Mach-O形式のスクリプティング機能追加の初期化関数


OSErr SAInitialize( CFBundleRef additionBundle ) {
    OSErr err;
    
    ...ここで初期化を行う...
    
    return err;
}



SAInitializeのadditionBundleパラメータはスクリプティング機能追加のバンドルを指します。この値を使って、バンドル内のリソースにアクセスすることができます。この値をグローバル変数などに保存すれば、初期化時以外のハンドラでバンドル内のリソースにアクセスすることができます。additionBundleはスクリプティング機能追加がロードされている間有効ですので、CFRetainを呼び出す必要はありません。

CFM形式のスクリプティング機能追加の場合、Code Fragmentの初期化ルーチンの名前を指定する方法は開発環境に依存しますので、開発環境の説明書をお読み下さい。Code Fragment Manager、CFMの初期化ルーチン、Mach-Oについてはこの後の参考文献をご覧下さい。

先頭に戻る



スクリプティング機能追加の終了

終了ルーチンでは、スクリプティング機能追加を終了させるために必要な処理を行います。特に、次の処理を欠かさずに行う必要があります。

  • 初期化ルーチンでインストールしたイベントハンドラやcoercionハンドラを削除する

  • スクリプティング機能追加で割り当てたメモリやリソースを解除する

スクリプティング機能追加の終了ルーチンは、AppleScriptがスクリプティング機能追加との接続をクローズするときに呼び出されます。このような接続のクローズは、スクリプティング機能追加がスクリプティング機能追加フォルダから外された後、AppleScriptが次に初期化されたタイミングです。

スクリプティング機能追加のロードやアンロードは“gdut”イベントが受理されたタイミングで行われます。AppleScriptはコンポーネントへの接続が開いた後、スクリプトをコンパイルする前にロードやアンロードを行います。ただし、システム終了時にアンロードは行われないので、終了ルーチンが実際に呼ばれるのは、スクリプティング機能追加がスクリプティング機能追加フォルダから外された後に、最初に“gdut”イベントが到達した時のみです。これはMac OS 9やMac OS Xも共通していますが、Mac OS Xの場合、スクリプティング機能追加はプロセスごとにアンロードされます。(スクリプティング機能追加を取り除いたあとは、その機能追加がロードされていたすべてのプロセスに対して“gdut”イベントを送信しないと、完全に除去できないことになります。Mac OS 9の場合は、どれか1つアプリケーションに対して“gdut”イベントを送信するだけで、完全に除去できました。)

リスト5は、スクリプティング機能追加の終了ルーチンの一例です。スクリプティング機能追加がCFM形式のバイナリとして提供されている場合は、CFMの終了ルーチンをスクリプティング機能追加の終了ルーチンに設定しなければなりません。スクリプティング機能追加がMach-O形式のバイナリとして提供されている場合、AppleScriptはスクリプティング機能追加をアンロードする直前にSATerminateを呼びます。



リスト5 スクリプティング機能追加の終了ルーチンの例


void SATerminate(void) {    
        
    AERemoveEventHandler(theAEEventClass,
                                 theAEEventID, gTheHandler, true);
                      
    DisposeAEEventHandlerUPP(gTheHandler);
    
    ...その他の終了作業...
                                 
}



CFM形式のスクリプティング機能追加の場合、終了ルーチンの設定方法は開発環境によって異なりますので、詳しくは開発環境の説明書をご覧下さい。CFMコードフラグメント、CFM初期化ルーチン、バンドル、Mach-O形式等の概念的な説明については、参考文献もご覧下さい。



リファレンスカウント

AppleScriptがスクリプティング機能追加をアンロードする際、まずスクリプティング機能追加が使用中かどうかを確認します。使用中の場合はアンロードすることができないので、アンロードは取り消されるか、使用状況に変化があるまで待ち状態になります。

AppleScriptは2つの方法で、スクリプティング機能追加の使用状況を確認します。CFM形式のスクリプティング機能追加はgAdditionReferenceCountと言うグローバル変数をエキスポートします。Mach-O形式のスクリプティング機能追加はSAIsBusyと言うルーチンをエキスポートします。CFM形式で使われているスクリプティング機能追加のgAdditionReferenceCount変数は、リスト6のSAIsBusyルーチンの結果と同じように扱われます。要約すると、gAdditionReferenceCountがゼロ以外の場合、そのスクリプティング機能追加は使用中の見なされ、アンロードされることはありません。



リスト6 スクリプティング機能追加のSAIsBusyルーチンの例


UInt32  gAdditionReferenceCount = 0;

Boolean SAIsBusy(void) {    
        
    return (gAdditionReferenceCount != 0);
                                 
}



すべてのAppleイベントハンドラとAppleイベントデータcoercionハンドラでは、実行時にgAdditionReferenceCountを1つ増やし、終了直前に1つ減らします。リスト7は典型的なハンドラルーチンの実装例です。



リスト7 gAdditionReferenceCountを管理する典型的なスクリプティング機能追加ハンドラルーチン


UInt32  gAdditionReferenceCount = 0;
     
     ....
     
OSErr MyEventHandler(const AppleEvent *ev,
                     AppleEvent *reply,
                     long refcon) {
    OSErr err;
          
        /* 実行時はリファレンスカウントを増やします */
    gAdditionReferenceCount++;
          
          ...ハンドラの実装コード...
          
        /* 終了直前にリファレンスカウントを減らします */
    --gAdditionReferenceCount;
          
    return err;
}
     


先頭に戻る



その他



Mac OS Xの実行環境

Mac OS Xの実行環境の場合、スクリプティング機能追加はAppleScriptに接続するアプリケーションが起動する度に、そのアプリケーションのアドレス空間にロードされます。このように、一つのスクリプティング機能追加が一度に複数ロードされることもあるので、プリンタやシリアルポートなどのシステムリソースを使用する場合はスクリプティング機能追加間で共有することも想定しなければなりません。



スクリプティング機能追加バンドルリソースの取得方法

Mach-Oバンドル形式で提供されているスクリプティング機能追加は、バンドル内のリソースやファイルにアクセスすることがあります。この場合は、SAInitializeで受け取ったCFBundleRefを保存して、ハンドラ内で利用します。




CFBundleRef gMyAdditionBundle;

OSErr SAInitialize(CFBundleRef additionBundle) {
    
        /* ハンドラ内でバンドル内のリソースにアクセスする場合は
        ここでバンドルへのリファレンスを保存します */
    gMyAdditionBundle = additionBundle;
    
    ....
    


スクリプティング機能追加では、受け取ったバンドルリファレンスに対してCFRetainを呼ぶ必要はありません。受け取ったバンドルリファレンスはスクリプティング機能拡張が開いている限り(SATerminateが呼ばれるまで)有効です。

スクリプティング機能追加のバンドル内の個々のリソースへのアクセス方法についてはこの後の参考文献をご覧下さい。



スクリプティング機能追加リソースファイルの取得方法

単一のCFMファイルとして提供されているスクリプティング機能追加は、ハンドラ実行中にリソースフォークへのアクセスが必要となることが多々あります。初期化ルーチン実行時にファイルのFSSpecを保存することで、後程リソースが必要となった時点でリソースフォークを開いてデータをロードすることができます。

Code Fragment Managerが初期化ルーチンを呼び出す際は、初期化ルーチンに渡されるCFragInitBlock内にFSSpecへのポインタを予め設定しています。スクリプティング機能追加では、このポインタをグローバル変数に保存しておくと便利です。




AliasHandle gMyAdditionLocation;

OSErr ConnectionInitializationRoutine(InitBlockPtr initBlkPtr) {
    OSErr err;
    
        /* 後程スクリプティング機能追加のリソースファイルを必要とする
        場合はここでファイルのFSSpecをグローバル変数に保存します。 */
    err = NewAlias(NULL,
                   initBlkPtr->fragLocator.u.onDisk.fileSpec,
                   &gMyAdditionLocation);
    if (err == noErr) {



スクリプティング機能追加がハンドラ内で開いたリソースファイルは、ハンドラ終了時までにクローズしなければなりません。また、ハンドラ内でリソースファイルを開く場合、リソースチェーンを変更しないような工夫が必要です。以下の例では、ハンドラ内でリソースファイルを開き、リソースチェーンの状態を保存しています。



     SInt16    oldResFile;
     SInt16    osaxResRef;
     FSRef     ref;
     
     oldResFile = CurResFile();
     osaxResRef = FSOpenResFile( &ref, fsRdPerm );

     // ここでリソースの読み込みなどの処理を行う

     CloseResFile( osaxResRef );
     UseResFile( oldResFile );


註:
スクリプティング機能追加のリソースファイルを特定するためのファイル参照番号はスクリプティング機能追加の初期化ルーチンで取得します。



ローカルシステムとリモートシステムからのイベント

リモートシステムからのイベントを検出して拒否するかどうかはそれぞれのスクリプティング機能追加ハンドラが決定します。ハンドラは、受信したイベントに含まれるkeyEventSourceAttr属性をチェックして、そのイベントのソースを判断できます。リモートシステムからのイベントは、kAERemoteProcessという属性値を持ちます。



     DescType   sourceAttr;
     DescType   actualType;
     Size       actualSize;

     anErr = AEGetAttributePtr( eventPtr, keyEventSourceAttr, typeType,
                                &actualType, &sourceAttr,
                                sizeof( sourceAttr ), &actualSize);
                                
     if ( sourceAttr == kAERemoteProcess ) {
     
          return errAEEventNotHandled;
          
     }
     

先頭に戻る



参考文献



先頭に戻る