DLLの関数を呼び出す(独自拡張)

関数および副プログラムをDLLを利用して定義することができる。内部,外部のどちらでもよい(DEF文には適用できない)。
DLLを利用して定義する関数(または,副プログラム)では, FUNCTION行(またはSUB行)と,END FUNCTION行(またはEND SUB行)の間に次の形式のASSIGN文を書く。

ASSIGN DLLファイル名 ,関数名
DLLファイル名,関数名は,文字列定数を書く。


FUNCTION GetVersion
ASSIGN "kernel32.dll","GetVersion"
END FUNCTION
DLLファイル名は,利用しようとする関数を含むDLLの名称を指定する。関数名は,DLL内で登録されている名称を指定する。関数名は,英字の大小の違いを識別するので注意。

数値型の引数は上位桁を切り捨てた32ビット整数として評価して渡す。副プログラムの場合であっても変数引数とはならず,常に値引数になる。
文字列型の引数は,1文字目へのポインタ(32ビット)として渡される。ただし,空文字列のときはヌルへのポインタではなく,ヌルを渡す。BASICの文字列はヌルで終端された文字列として利用できる。
引数は,末尾の引数から順にスタック上に積まれる。
数値関数として定義するときは,DLLの関数から制御が戻されたときのEAXレジスタの値が関数値となる。この値は,常に32ビットの符号付整数として解釈する。
文字列関数として定義する場合,対象とするDLL関数は,ヌル終端のASCII文字列へのポインタを返す関数でなければならない。

例1
DECLARE EXTERNAL FUNCTION MesBox
LET n=MesBox(0,"Hello","BASIC",3)
PRINT n
END
EXTERNAL FUNCTION MesBox(owner,text$,caption$,flag)
ASSIGN "user32.dll","MessageBoxA"
END FUNCTION

変数のアドレスを引数として要求するDLL関数を呼び出すときは,文字列変数を引数にすることができる。
REPEAT$関数などを利用して,引数として与える文字列変数の文字数をDLLの関数が必要とする大きさ以上としておく。
この変数の値は他の変数から代入して作成したものであってはならず,また,この変数は他の変数に代入したことのあるものであってもならない。
受け取った値は部分文字列指定を利用して取り出す。
この変数を使用するプログラム単位でOPTION CHARACTER BYTEを宣言しておけば,文字列変数をバイト単位のバッファとして使える。
例2
DECLARE EXTERNAL FUNCTION CurrDir$
PRINT CurrDir$
END
EXTERNAL FUNCTION CurrDir$
OPTION CHARACTER BYTE
FUNCTION GetCurrentDirectory(n,s$)
ASSIGN "kernel32.dll","GetCurrentDirectoryA"
END FUNCTION
LET s$=Repeat$(" ", 200)
LET n=GetCurrentDirectory(200,s$)
LET CurrDir$=s$(1:n)
END FUNCTION

新規にダイアログを生成するDLLを実行するとテキスト出力の生成が阻害されることがある。その種のDLLを利用する場合は,次の形式のASSIGN文を書く。
ASSIGN DLLファイル名 ,関数名 ,GUI

実数値を取るDLL関数を利用する場合は,次の形式のASSIGN文を書く。
ASSIGN DLLファイル名 ,関数名 ,FPU
この形のASSIGN文を実行すると,FPUのスタックトップレジスタをPOPし,その値を関数の値とする。

CDECL呼出規約でコンパイルされたDLLを呼び出すときは,次の形式のASSIGN文を書く。
ASSIGN DLLファイル名 ,関数名 ,CDECL
ASSIGN DLLファイル名 ,関数名 ,GUI, CDECL
ASSIGN DLLファイル名 ,関数名 ,FPU, CDECL
Linux(32ビット)では,暗黙でCDECLを仮定する。StdCall呼び出し規約で作成されたDLLを呼び出すときは,上記のCDECLの替わりにSTDCALLを指定する。

<補足> DLLからOSの例外を受け取ると,ASSIGN文でExtype=-9900の例外を生成する。但し,DLLで発生する可能性のある例外はDLLの内部で処理しておくべきである。
<補足> ASSIGN文はFPUの制御wordを保存しないので,DLLでFPUの制御wordを変更したときは,戻る前に復元しなければならない。
<Note> DLLは翻訳時にロードされる。指定されたDLLが他のDLLにリンクしていてそのDLLがロードできないためにエラーになる場合でも,プログラムで指定したDLLの名称で文法の誤りを表示する。
<Note> 引数の個数の誤りは翻訳時にも実行時にもチェックされない。引数の個数の誤りは悲惨な結果をもたらす。

<参考> 32ビットの符号つき整数nを符号なし整数の16進表記文字列に変換する関数
BSTR$(MOD(n, 4294967296),16)。

<参照> ORD関数とCHR$関数,部分文字列指定
<補足> Linux(64ビット)でのASSIGN文は暫定仕様。32ビットの制限がある。
<補足> Mac版はLinux版と同仕様だが,動作は未確認。


組込み関数(Windowsのみ)

ANSI$(文字列式)
 WindowsのANSI文字(Shift-JIS)に変換する。

10 DECLARE EXTERNAL FUNCTION MesBox$
20 CALL MesBox(0,ANSI$("漢字"),ANSI$("BASIC"),3)
30 END
100 EXTERNAL SUB MesBox(owner,text$,caption$,flag)
110 ASSIGN "user32.dll","MessageBoxA", GUI
120 END SUB

WIDE$(文字列式)
 WindowsのWide文字(UTF-16)に変換する。

10 DECLARE EXTERNAL FUNCTION MesBox$
20 CALL MesBox(0,WIDE$("髙木"),WIDE$("BASIC"),3)
30 END
100 EXTERNAL SUB MesBox(owner,text$,caption$,flag)
110 ASSIGN "user32.dll","MessageBoxW", GUI
120 END SUB

ImportANSI$(文字列式)
 WindowsのANSI文字(Shift-JIS)を内部形式(UTF-8)に変換する。

ImportWIDE$(文字列式)
 Wide文字(UTF-16)を内部形式(UTF-8)に変換する。

WINHANDLE(文字列式)
  Win32APIに渡すための,BASICシステム自身のウィンドウ(またはコントロール)のハンドル値。
  引数は,"MAIN", "TEXT", "GRAPHICS", "INPUT", "CHARACTER INPUT","TRACE", "LOCATE", "LOCATECHOICE"のいずれか。
 大文字と小文字の違いは無視する。
上記以外を指定すると値が0になる(例外を生成しないので注意)。

100 DECLARE EXTERNAL SUB MoveTextWindow, ResizeTextWindow 
110 FOR i=1 TO 5
120    PRINT "Hello"
130    CALL MoveTextWindow(i*100,i*60)
140    CALL ResizeTextWindow(320,160+i*20)        
150    WAIT DELAY 1
160 NEXT i
170 END
180 EXTERNAL SUB MoveTextWindow(x,y)
190 SUB SetWindowPos(hwnd,HwndInsAfter,x,y,cx,cy,nFlags)
200    ASSIGN "user32.dll","SetWindowPos"
210 END SUB
220 CALL SetWindowPos(WinHandle("TEXT"),0,x,y,0,0,1)
230 END SUB
240 EXTERNAL SUB ResizeTextWindow(x,y)
250 SUB SetWindowPos(hwnd,HwndInsAfter,x,y,cx,cy,nFlags)
260    ASSIGN "user32.dll","SetWindowPos"
270 END SUB
280 CALL SetWindowPos(WinHandle("TEXT"),0,0,0,x,y,2)
290 END SUB