OLEオートメーション(OCXの利用) 

 (仮称)十進BASIC(ver.5.3.5)では,OLEオブジェクトをFull BASICのモジュールに対応させて利用する。

 次の条件を満たせば,ActiveXなどのOLEコントロール(OCX)の利用も可能。
  非表示(親ウィンドウ不要)
  ディスパッチインターフェースに対応している
BASICから直接利用するのが難しいOCXは,自作DLLを使うを参照して,Delphi,VBなどを利用してAPIに変換するDLLを作成してください。

OLEオブジェクトのモジュールへの割り当て

 モジュール名は任意の名前でよい。
 モジュール本体にProgIDを引数とするOLE CretaeObject文を書く。引数はProgIDを引用符で括って書く。この文は,OLEオブジェクトの生成を意味するが,実際には翻訳時に実行される。  

 メソッドは,モジュールの外部手続きに割り当てる。手続き名は任意。
 結果を返すメソッドは外部関数に割り当て,external function行とend function行の間にOLE METHOD文を書く。
 結果を返さないメソッドは外部副プログラムに割り当て,external sub行とend sub行の間にOLE METHOD文を書く。
 OLE METHOD文の引数には,引用符で括ってメソッド名を書く。
 手続きの引数の個数と型(数値と文字列の別)は,割り当てるメソッドのそれと一致させる。

 プロパテイーの設定は外部副プログラムに割り当てる。external sub行とend sub行の間にOLE PROPERTYPUT文を書く。その引数には,引用符で括ってプロパティ名を書く。副プログラムの引数が設定すべき値になる。

 プロパティーの読み出しは,外部関数に割り当てる。external function行とend function行の間にOLE PROPERTYGET文を書く。その引数には,引用符で括ってプロパティ名を書く。

 真偽値は,数値で代用する。OLEでは,真は-1,偽は0と定義されている。

メソッド名,プロパティー名は翻訳時に検査する。引数の型と個数の誤りは実行時に検出される。

(Note)複素数には対応していない。その他,いくつか対応できないデータ型がある。
    値を返すメソッドで引数として書いた変数の値を書き換えるものには対応していない。
    配列引数には対応していない。

例1

MusicPlayctrl.ocx を使う。

このOCXの説明書にはProgIDが明示されていないので,OCXファイル名でレジストリ内を検索して探す(キー値がProgIDのエントリーにProgIDが書いてある)。

100 DECLARE EXTERNAL FUNCTION Music.MusicPlay$
110 PRINT Music.MusicPlay$("E04 U04 P04 G02 ",0,2400)
120 PRINT Music.MusicPlay$("G04 P04 U04 E02 ",0,1600)
130 END
140
150 MODULE Music
160 PUBLIC FUNCTION MusicPlay$
170 OLE CreateObject "MusicPlayCtrl.Music"
180 EXTERNAL FUNCTION MusicPlay$(s$,Handle,Long)
190    OLE METHOD "MusicPlay"
200 END FUNCTION
210 END MODULE
 モジュールで定義された外部関数をモジュール外から使うために160行に示すようなPUBLIC FUNCTION文を書く。
モジュールで定義された外部関数を使うプログラム単位には,100行に示すようなDECLARE EXTERNAL FUNCTION文を書く。
DECLARE EXTERNAL FUNCTION文には,「モジュール名.関数名」の形で外部関数名を宣言する。

戻り値を持つメソッドでも,戻り値が不要なら副プログラムに割り当ててもよい。

100 DECLARE EXTERNAL SUB Music.MusicPlay
110 CALL Music.MusicPlay("E04 U04 P04 G02 ",0,2400)
120 CALL Music.MusicPlay("G04 P04 U04 E02 ",0,1600)
130 END
140 
150 MODULE Music
160 PUBLIC SUB MusicPlay
170 OLE CreateObject "MusicPlayCtrl.Music"
180 EXTERNAL SUB MusicPlay(s$,Handle,Long)
190    OLE METHOD "MusicPlay"
200 END SUB
210 END MODULE
 モジュールで定義された外部手続きをモジュール外から使うために160行に示すようなPUBLIC SUB文を書く。
 モジュールで定義された外部副プログラムを使うプログラム単位には,100行に示すようなDECLARE EXTERNAL文を書く。

例2

HiroCalcを使う。

プロパティの設定は,220行に示すようにモジュールの外部副プログラムに割り当てる。

100 DECLARE EXTERNAL FUNCTION Calc.calc$
110 DECLARE EXTERNAL SUB Calc.SetFormat
120 CALL SetFormat("#####.##")
130 PRINT calc$("12+3")
140 END
150 MODULE Calc
160 OLE CREATEOBJECT "Hiro_Calc.HiroCalc"
170 PUBLIC FUNCTION calc$
180 PUBLIC SUB SetFormat
190 EXTERNAL FUNCTION calc$(s$)
200    OLE METHOD "calc"
210 END FUNCTION
220 EXTERNAL SUB SetFormat(s$)
230    OLE PROPERTYPUT "CalcFormat"
240 END SUB
250 END MODULE 
DECLARE EXTERNAL文で宣言した識別名は,「モジュール名.」を省いて書くことができる。
モジュール名を付加して書くと,120行,130行は
120 CALL Calc.SetFormat("#####.##")
130 PRINT Calc.calc$("12+3")
のようになる。

例3

ATLDosを使う。
100 DECLARE EXTERNAL SUB ATLDos.Start, ATLDos.End,  ATLDos.SetSync
110 DECLARE EXTERNAL FUNCTION ATLDos.SendCommand$, ATLDos.Sync
120 CALL Start     
130 CALL SetSync(-1)            ! 同期モードに設定   
140 PRINT SendCommand$("ECHO OFF")       
150 PRINT SendCommand$("DIR")            
180 PRINT "Sync=";Sync          ! プロパティ読み出しの実行例       
190 CALL End                             
200 END
210  
220 MODULE ATLDos
230 OLE CREATEOBJECT "ATLDos.CmdExec"
240 PUBLIC FUNCTION SendCommand$, Sync
250 PUBLIC SUB Start, End, SetSync
260  
270 EXTERNAL SUB Start
280    OLE METHOD "Start"
290 END SUB
300  
310 EXTERNAL SUB END
320    OLE METHOD "End"
330 END SUB
340  
350 EXTERNAL FUNCTION SendCommand$(s$)
360    OLE METHOD "SendCommand"
370 END FUNCTION 
380  
390 EXTERNAL SUB  SetSync(n)
400    OLE PropertyPut "Sync"
410 END SUB
420  
470 EXTERNAL FUNCTION Sync
480    OLE PropertyGet "Sync"
490 END FUNCTION
500  
510 END MODULE
Full BASICにはプロパティという概念がないので,プロパティへの値の設定,プロパティの値の読み出しは,それぞれ副プログラム,関数の形で利用する。

Full BASICは真偽値をデータとして利用することができない言語であるが,マイクロソフト社のBool型,Boolean型は数値の型の一部なので,-1(あるいは非ゼロ)を真,0を偽として扱えば数値で代用できる。

非同期で動作するOCXの利用

 (仮称)十進BASICはプログラムの実行が終わるすぐにオブジェクトを削除するので,非同期で動作するOCXを使う場合は,OCXの動作が終了するまでプログラムの実行を終えないようにする。

例4 MusicPlayctrl.ocx を非同期で使う

100 DECLARE EXTERNAL SUB Music.MusicPlay
110 CALL MusicPlay("E04 U04 P04 G02 ",0,2400,-1)
120 WAIT DELAY 3
130 CALL MusicPlay("G04 P04 U04 E02 ",0,1400,-1)
140 PAUSE "終了したらEnterキーを押してください"
150 END
160  
170 MODULE Music
180 PUBLIC SUB MusicPlay
190 OLE CreateObject "MusicPlayCtrl.Music"
200 EXTERNAL SUB MusicPlay(s$,Handle,Long,Sync)
210    OLE METHOD "MusicPlay"
220 END SUB
230 END MODULE

ProgIDの探し方

ProgIDが明示されていないActiveXを使う場合は,サンプルアプリケーションのソースから推測する。
VB向けのサンプルがあれば,CreateObjectを探す。
Set obj = CreateObject("ATLDos.CmdExec")
のような記述があれば,CreateObjectの引数がProgID。

CreateObjectが見つからない場合は,レジストリ内を検策して探す。
レジストリエディタは, スタートメニューの「ファイル名を指定して実行」に regedit と入力すれば起動する。
レジストリの編集メニューの検索で,OCXのファイル名を入力して,データの欄にチェックを入れて検索する。
レジストリ内では,HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\以下にProgIDが登録されている。
レジストリを変更してしまうとWindowsの動作に支障がでるので,誤って値を書き換えてしまわないように注意。

例5 tlbinf32.dll

tlbinf32.dllはVBなどに付録する(らしい?)タイプライブラリを読むためのOLEサーバDLL。
tlbinf32.dllが登録されていれば,次のプログラムでOCXのイベントインターフェースのGUIDとイベント処理メソッドのIDを知ることができる。

ProgIDがないOLEサーバは,1350行に示すようにGUIDを指定して使う。
オブジェクトを動的にモジュールに割り当てるときはOLE CREATEOBJECTの代わりにOLE ASSIGNを使う。
値がオブジェクトであるプロパティの取得を数値関数に割り当てると,この関数を実行したとき,内部的にオブジェクトを一つ作成し,それを参照するための数値を返す。
OLE ASSIGN文の引数には,その値(すなわち,プロパティから取得したオブジェクトへの参照を意味する数値)を指定する。
OLE ASSIGN文は実行文なので,モジュール内の外部手続きで実行することにしてもよい。

1000 DECLARE EXTERNAL FUNCTION TLI.CoClasses, TLI.CoClass
1010 DECLARE EXTERNAL FUNCTION Collection.Count
1020 DECLARE EXTERNAL FUNCTION CoClass.DefaultInterface,CoClass.DefaultEventInterface,CoClass.Name$
1030 DECLARE EXTERNAL FUNCTION Interface.members,Interface.Name$,Interface.GUID$
1040 DECLARE EXTERNAL FUNCTION Member.Name$,Member.MemberID
1050 DECLARE EXTERNAL SUB TLI.LoadFromFile, TLI.LoadRegTypeLib
1060 DECLARE EXTERNAL SUB CoClass.SetCoClass
1070 DECLARE EXTERNAL SUB Interface.SetIntf
1080 DECLARE EXTERNAL SUB Member.SetMember
1090 DECLARE NUMERIC i,j
1100 DECLARE STRING FName$
1110 
1120 FILE GetName FName$
1130 CALL TLI.LoadFromFile(FName$)
1150 FOR i=1 TO Collection.Count(TLI.CoClasses)
1160    CALL CoClass.SetCoClass(TLI.CoClass(i))
1170    CALL Interface.SetIntf(CoClass.DefaultEventInterface)
1180    PRINT "*DefaultEventInterface*"
1190    WHEN EXCEPTION IN
1200       PRINT CoClass.Name$,Interface.NAME$,Interface.GUID$
1210       PRINT "** Member **"
1220       FOR j=1 TO Collection.Count(Interface.members)
1230          CALL Member.SetMember(Interface.Member(j))
1240          PRINT member.NAME$, member.MemberID
1250       NEXT j
1260    USE
1270       PRINT CoClass.name$;" has no DefaultEventInterface"
1280    END WHEN  
1290 NEXT i
1300 END
1310 
1320 MODULE TLI
1330 PUBLIC FUNCTION CoClasses,CoClass
1340 PUBLIC SUB LoadFromFile, LoadRegTypeLib
1350 OLE CreateObject "{8B217746-717D-11CE-AB5B-D41203C10000}"
1360 EXTERNAL SUB LoadFromFile(s$)
1370    OLE PropertyPut "ContainingFile"
1380 END SUB
1390 EXTERNAL SUB LoadRegTypeLib(GUID$,ma,mi,LCID)
1400    OLE METHOD "LoadRegTypeLib"
1410 END SUB
1420 EXTERNAL FUNCTION CoClasses
1430    OLE PropertyGet "CoClasses"
1440 END FUNCTION
1450 EXTERNAL FUNCTION CoClass(n)
1460    OLE propertyGet "CoClasses"
1470 END FUNCTION
1480 END MODULE 
1490 
1500 MODULE CoClass
1510 PUBLIC SUB SetCoClass
1520 PUBLIC FUNCTION DefaultInterface,DefaultEventInterface,Name$
1530 EXTERNAL SUB SetCoClass(n)
1540    OLE ASSIGN n
1550 END SUB
1560 EXTERNAL FUNCTION DefaultInterface
1570    OLE PROPERTYGET "DefaultInterface"
1580 END FUNCTION
1590 EXTERNAL FUNCTION DefaultEventInterface
1600    OLE PROPERTYGET "DefaultEventInterface"
1610 END FUNCTION
1620 EXTERNAL FUNCTION name$
1630    OLE PROPERTYGET "Name"
1640 END FUNCTION 
1650 END MODULE 
1660 
1670 MODULE Interface
1680 PUBLIC FUNCTION member,Members,Name$,GUID$
1690 PUBLIC SUB SetIntf
1700 EXTERNAL SUB SetIntf(n)
1710    OLE ASSIGN n
1720 END SUB 
1730 EXTERNAL FUNCTION Name$
1740    OLE PropertyGet "Name"
1750 END FUNCTION
1760 EXTERNAL FUNCTION GUID$
1770    OLE PropertyGet "GUID"
1780 END FUNCTION
1790 EXTERNAL FUNCTION member(n)
1800    OLE PROPERTYGET "Members"
1810 END FUNCTION
1820 EXTERNAL FUNCTION members
1830    OLE PROPERTYGET "members"
1840 END FUNCTION
1850 END MODULE
1860 
1870 MODULE member
1880 PUBLIC FUNCTION Name$,MemberID
1890 PUBLIC SUB setmember
1900 EXTERNAL SUB setmember(n)
1910    OLE ASSIGN n
1920 END SUB
1930 EXTERNAL FUNCTION Name$
1940    OLE PropertyGet "Name"
1950 END FUNCTION
1960 EXTERNAL FUNCTION MemberID
1970    OLE Propertyget "MemberID"
1980 END FUNCTION
1990 END MODULE 
2000  
2010 MODULE Collection
2020 ! Countプロパティを持つオブジェクトのCountプロパティを取得する汎用モジュール
2030 PUBLIC FUNCTION Count
2040 SHARE FUNCTION GetCount       ! GetCountはモジュール内でのみ使う
2050 EXTERNAL FUNCTION Count(n)
2060    OLE ASSIGN n
2070    LET COUNT=GetCount
2080 END FUNCTION 
2090 EXTERNAL FUNCTION GetCount
2100    OLE PROPERTYGET "Count"
2110 END FUNCTION
2120 END MODULE
<Note>オブジェクトを取り出すプロパティ取得とOLE ASSIGN文の実行とは一対一に対応しなければならない。
プロパティから取得したオブジェクトへの参照を数値変数に保存しておくことはできる(できてしまう)が,それを2回以上のOLE ASSIGN文の実行で使ってはならない。そのような状況では,OLE ASSIGN文の実行のたびごとにプロパティからオブジェクトを取得すること。

イベント処理

 イベント処理が必要なOCXを使う場合は,OLE CreateObject文のProgIDに続いて,コンマで区切って,イベント インターフェースのGUIDの文字列表現を文字列定数の形で(つまり,引用符で括って)書く(例6のを210行を参照)。
 OCXからのイベントを受け取る手続きには,例6の380行のような形式でその手続きに割り当てるメソッドのDispIDを書く。(例6のRecvPrompt副プログラムは,DispIDが1のメソッドに割り当てている。)
 (仮称)十進BASICの現在のバージョンは異なるスレッドから手続きが呼ばれても正しく動作するようにできていない。OCXが非同期でイベント処理手続きを呼び出す場合は,BASIC本来のスレッドはWAIT文かPAUSE文を実行して実質的に休止状態にしておかなければならない。

例6 ALTDosを非同期で使う

100 DECLARE EXTERNAL SUB ATLDos.SendCommand,ATLDos.Start, ATLDos.End 
110 DECLARE EXTERNAL NUMERIC ATLDos.count
120 CALL ATLDos.Start     
130 CALL ATLDos.SendCommand("ECHO OFF")       
140 CALL ATLDos.SendCommand("DIR")            
150 PAUSE 
160 PRINT count; "回呼ばれた"            
170 CALL ATLDos.End                 
180 END
190  
200 MODULE ATLDos
210 OLE CREATEOBJECT "ATLDos.CmdExec","{7BD49A5F-DCC2-11D5-86CB-00E0297583A4}"
220 PUBLIC NUMERIC count
230 PUBLIC SUB Start, End, SendCommand
240 LET count=0
250  
260 EXTERNAL SUB Start
270    OLE METHOD "Start"
280 END SUB
290  
300 EXTERNAL SUB END
310    OLE METHOD "End"
320 END SUB
330  
340 EXTERNAL SUB SendCommand(s$)
350    OLE METHOD "SendCommand"
360 END SUB  
370  
380 EXTERNAL SUB RecvPrompt(s$) ,DispID 1
390    WHEN EXCEPTION IN
400       PRINT s$
410       LET count=count+1
420    USE
430        
440    END WHEN
450 END SUB
460  
470 END MODULE
OCXから呼ばれる手続きでは,410〜460行に示すように手続き内で例外を処理しておくことが望ましい(十進BASICは未処理の例外があると,呼び出し元に例外を生成すべきことを伝えるが,それ以後は相手しだい)。
OCXから呼ばれる手続きでSTOP文を実行しても,プログラムは停止しない(副プログラムの実行を中断して,呼び出し元に例外を生成すべきことを伝えるが,それ以後は相手しだい)。

<Note>イベント インターフェースのGUIDはWindowsのレジストリから直接に読み取ることはできない。各イベントのDispIDも同様。
したがって,マニュアルに記載がない場合,タイプライブラリを利用することのできるソフト(VC++やDelphiなど)が必要。tlbinf32.dllを入手できれば,(レジストリに登録して)例5のプログラムを実行することで必要な情報は得られる。
なお,見かけ上よく似た文字列がClassID(CLSID),TypeLibを表すためにも用いられるので,混同しないように注意。
たとえば,VB向けのサンプルでobjectに続けて書いてあるGUIDはおそらくTypeLib。

<注意> 複数のスレッドから同時に割り込まれると正常に動作しないので,割り込み元が複数のスレッドに存在するような使い方はしないでください。


シグナル

 非同期で呼ばれるイベント処理手続きが処理の終了を知らせるのにシグナルを使うことができる。
 シグナルは英字で始まる名前で識別する(引用符で括らない)。

SIGNAL シグナル名
 シグナル名で識別されるシグナルを発する。

WAIT SIGNAL シグナル名
 シグナル名で識別されるシグナルが発せられるまで待機する。

WAIT SIGNAL シグナル名 TIMEOUT 数値式
 数値式で指定される時間(秒)が経過するか,またはシグナルを受信するまで待機する。



戻る