Topic 3: ActiveBasicで作ったDLLをVisualBasicで使用する

ActiveBasicでは,DLLを作成することが出来ます。(作成方法はここでは述べません。講座の方で今後やろうと思います。)
そのDLLは,他のDLLと変わるところはないわけですが,これをActiveBasic以外で使うとなると,分からない方もいらっしゃるかと思います。
そこで,実際に1つのDLLをActiveBasic以外のプログラム言語,例えばVisualBasicで動かしてみることにします。

今回は「MidoriDLL」を使わせていただきます(制作者:みどりさん)
ABですべてコードを書くと面倒になるような処理を,まとめて行えるDLLです。
***みどりさん,ありがとうございます***

こちらからソースコードをダウンロードできます。mdllvb.lzh
MidoriDLL自身は入っていませんので,上記のみどりさんのサイト(トップページ→「Works」→「Program」→「ProgramBox」)でダウンロードして,実行ファイルと同じフォルダに入れて下さい。MidoriDLLは現在公開一時停止中とのことです。

それでは各コードの解説です。

Private Declare Function GetPrice Lib "Midori_DLL" (ByVal price As Long) As Long
Private Declare Function GetFolderD Lib "Midori_DLL" (ByVal hWnd As Long, ByVal lpFolder As String) As Long
Private Declare Sub textdraw Lib "Midori_DLL" (ByVal hDC_TD As Long, ByVal X As Long, ByVal Y As Long, _
    ByVal buf As String, ByVal buf2 As Long, ByVal FontSize As Long, ByVal Font As String, ByVal color As Long)
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As String) As Long

Private Sub Command1_Click()
Dim Buffer As String, OriginalPrice As Long, TaxedPrice As Long
    'アプリケーションのあるフォルダへ移動
    ChDir App.Path
    ChDrive App.Path

    '税抜き価格を入力
    Buffer = InputBox("税抜きの価格は?", "消費税の計算")

    If Buffer <> "" Then
        OriginalPrice = Val(Buffer)

        'DLLの関数を実行
        TaxedPrice = GetPrice(OriginalPrice)

        '結果を表示
        Text1.Text = OriginalPrice & " 円のものの税込み価格は " & TaxedPrice & " 円です。"
    End If
End Sub

Private Sub Command2_Click()
Dim SelectedFolder As String, SelectFlag As Long
    'アプリケーションのあるフォルダへ移動
    ChDir App.Path
    ChDrive App.Path

    'バッファを確保
    SelectedFolder = String(260, Chr(0))

    'DLLの関数を実行
    SelectFlag = GetFolderD(MForm.hWnd, SelectedFolder)

    If SelectFlag Then
        'バッファの余分な部分を削除
        SelectedFolder = StrConv(LeftB(StrConv(SelectedFolder, vbFromUnicode), lstrlen(SelectedFolder)), vbUnicode)

        '結果の表示
        Text2.Text = "あなたが選択したフォルダは " & SelectedFolder & " です。"
    Else
        'フォルダを選択しなかった場合
        Text2.Text = "フォルダは選択されませんでした。"
    End If
End Sub

Private Sub Command3_Click()
Dim Buffer As String, FontFace As String
    'アプリケーションのあるフォルダへ移動
    ChDir App.Path
    ChDrive App.Path

    '表示する文字列/フォント名を指定
    Buffer = InputBox("表示する文を入力して下さい。", "文字を画像扱いで表示", "MidoriDLL('∇'*)")
    FontFace = "MS P明朝"

    'DLLの関数を実行
    textdraw Picture1.hDC, 0, 0, Buffer, LenB(StrConv(Buffer, vbFromUnicode)), 22, FontFace, RGB(255, 0, 255)
End Sub

Private Sub Command4_Click()
    '描画画面を消去
    Picture1.Cls
End Sub

1.GetPrice(消費税の計算)

ものすごく簡単なものですが,とりあえずやってみました。
ActiveBasic上ではこの関数は「Function GetPrice (price As Long) As Long」と定義されていますが,これはVisualBasic上では「Function GetPrice (ByVal price As Long) As Long」と定義する必要があります。
なぜなら,ABでは関数の引数は特に指定しなければ値渡し(関数に対しては値のみを渡し,関数の中でその変数の値が変更されても元の変数の値は変わらない。"ByVal" で表す)で引数が渡されるのに対し,VBでは参照渡し(関数に対して与えた引数の値が関数の中で変更された場合,元の変数の値も変更される。"ByRef" で表す)がデフォルトになっているからです。
もちろんこの場合はDLLの宣言ですので,「Private Declare Function GetPrice Lib "Midori_DLL" (ByVal price As Long) As Long」と定義すればよいです。他も同様で,すべて ByVal が必要です。

2.GetFolderD(フォルダ選択ダイアログを表示)

AB上では,宣言は「Function GetFolderD (hWnd As Long, lpFolder As BytePtr) As Long」となっています。が,VBにBytePtr型はありません。しかしVB上でBytePtrの代わりにStringで指定すると,VBの方で自動的にBytePtrとStringとのやりとりを行ってくれるようです。ですのでここでは "lpFolder As String" と定義しています。

実行するには,まずバッファを確保しなければなりません。(DLLの中では,文字列を保存する領域を自動的には確保できないからです。)ABでは ZeroString 関数を使うことが多いですが,VBでは ZeroString 関数はないので,String 関数を利用しています。(ABでは String$ や Chr$ と書くところを,VBでは '$' を抜いて書けます。トピック1をご覧になって下さい。)なお,長さが "260" になっているのは,ABの MAX_PATH 定数(ファイルパスの最大の文字列長)の値に合わせてあります。

この時,例えばダイアログで「C:\Windows」というフォルダを選択した場合,SelectedFolder には以下のように文字列が格納されます。(「NULL」は Chr(0) のこと)

バイト 1 2 3 4 5 6 7 8 9 10 11 12 ・・・ 259 260
文字 C : \ W i n d o w s NULL NULL ・・・ NULL NULL

この理由から,フォルダ名を取得した後に「NULL文字を検出して,その場所以降を取り除く」という処理を行っています。こうしないと,SelectedFolder の後ろに文字列を連結してもNULL文字のために以後の部分が表示されなくなります。

ここで役立つのが,ABでたまにお世話になる lstrlen 関数です。VBで単に Len 関数を使った場合,NULL文字がある部分も文字数を数えてしまうので,NULL文字以下を落としてしまう lstrlen が役に立ちます。(ABでは自動的に定義してくれますが,VBではDeclareで定義する必要があります。ABの「api_system.sbp」の428行目の Declare 文をVBにそのまま持って来て「Private」をつけると使えます。)
ここで厄介なのは,VBでは文字コードが「Unicode」といって,1文字を2バイトか4バイトで表す方式を用いています(半角文字や日本語の文字はすべて2バイトです)。これに対してDLL中(AB中も同じ)の文字コードはShift-JISであり,1文字は半角1バイト/全角2バイトです。この場合,

StrConv(LeftB(StrConv(SelectedFolder, vbFromUnicode), lstrlen(SelectedFolder)), vbUnicode)
              |----------------(1)-----------------|  |---------(2)---------|
        |--------------------------------(3)--------------------------------|
|------------------------------------------(4)-------------------------------------------|

とすることで,元の文字列をShift-JISに変換し(1)たものの,左側から(3)Shift-JISで数えたバイト数(2)分だけ取り出し,それをUnicodeに戻す(4)ということを表しています。
(なお,DLLで定義された関数へ文字列を引渡す場合,文字コード変換はVBで自動的に行います。)

※追記:lstrlenW(Unicode文字列の文字列長を取得する)という関数も存在するのですが,それを単純に使ってもなぜかうまくいきませんでした・・・。原因が判明したら書きます。

ついでにですが,GetFolderD関数に与えている「MForm.hWnd」は,MForm(このプログラムのメインウィンドウの名前)のウィンドウハンドルを表します。VBではAPIを使わなくてもウィンドウハンドルが取得できるのです。

3.textdraw(文字を画像として表示)

こちらも,AB上にあってVB上にないDWord型を利用しています(hDC_TD)。これは同じ4バイト整数であるLongで代用します。BytePtrをStringに変えてよいのはGetFolderDのときと同じです。

ここでも,VBでは文字コードにUnicodeを使っているのに対しDLL中の文字コードはShift-JISですから厄介なことが生じます。文字列の引渡しにおける文字コード変換はVBが行ってくれますが,問題は第5パラメータの「buf2 As Long」です。これは文字列の長さ(バイト数)を指定するものですが,当然これはShift-JISでのバイト数を指定しなければなりません。
そこで,LenB(StrConv(Buffer, vbFromUnicode)) というややこしいコードを使っています。(コードはhiraさんの提供です。ありがとうございました)これは,文字列をShift-JISに変換してからそのバイト数を受け取ります。
なお,ウィンドウハンドルのときと同様,Picture1.hDC のようにすることでデバイスコンテキストを取得できます。
ちなみに,Picture1.AutoRedraw(Picture1 の画像を,VBで自動的に再描画するか)は False にしておかないとうまく動作しません。(再描画の方法が分からないので,分かりましたら報告します。)

また別のDLLの実験に成功しましたら,報告します。

1つ前へ ActiveBasic Hall トップへ ActiveBasic Center トップへ Sinryow Game トップへ