logo7

 Allergy Design Office > VB & VBAプログラミング

VB & VBAプログラミング

 Visual BasicはAPI、DLL、OCX等と組み合わせて使うことによって、実に多くのことができる。Visual Basicを使えるようになると、Excel等のマクロであるVBAも使えるようになるという副産物もある。(もちろんこの逆もありだ。)Visual Basicで作れないものはないと私は思う。たしかに、速度を要求されるようなプログラムは無理があるが、そこは適宜DLLやATLを併用する。それに最近ではDirect3Dだって使える!

 本文中の記述例およびサンプルは、すべてVB6またはVBA5(Excel97)で書いてある。

 記述例およびサンプルに含まれるファイルの全部、または一部を使用したことによる損害等について、一切の責任を負いません。

  1. [VB]テキストボックスを自動的にスクロール
  2. [VB]コマンドラインの取得
  3. [VB]コンソールアプリケーションの起動と完了待ち
  4. [VB]Excelの起動
  5. [VB]円形のスクリーンを描画する
  6. [VB]App.Path
  7. [VB]高速なファイルの読み込み
  8. [VB,VBA]オートシェイプを描画する
  9. [VBA]標準メニューに項目を追加する
  10. [VBA]画面更新を抑止して高速化
  11. [VBA]数式を設定する
  12. [VBA]ソートを行う
  13. [VBA]検索を行う
  14. [VBA]ThisWorkbook.Path

[VB]テキストボックスを自動的にスクロール

 テキストボックスに文字列を随時追加し、常に最後尾の文字列が表示されるように自動的にスクロールさせる。つまり、昔のBASICで言うならPRINT文をイメージしていただければ良い。テキストボックス内の文字列の末尾に文字列を追加するにはSelTextプロパティに文字列を設定するのが便利である。

 使用するテキストボックスは予めMultiLineプロパティをtrueにしておく。必要に応じてScrollBarsプロパティも設定する。テキストボックス内の文字列を任意の位置で改行させたければvbCrLfを連結すれば良い。SelTextプロパティを設定すれば自動的に表示位置もスクロールされて書き込んだところが表示されるようになる。

    txtOut.SelText = "Allergy testing" & vbCrLf

 もちろん&演算子を用いて通常の文字列と同じように連結しても良い。その場合は以下のようになる。テキストボックスへの文字列の追加が済んだら、SelStartプロパティを設定して追加した最後尾(最新)の文字列を画面に表示させる。このとき最後尾の文字列が画面に表示されていなければ、テキストボックス内の文字列が自動的にスクロールされ、設定したSelStart辺りの文字列が表示されるようになる。

    txtOut.Text = txtOut.Text & "Allergy testing" & vbCrLf
    txtOut.SelStart = Len(txtOut)

 ただし、こちらは毎回表示位置を再設定しているため、テキストが増えてくると非常に遅くなる。SelTextのほうは高速だが、ユーザがカーソルを移動しないことが前提である。

[VB]コマンドラインの取得

 VBでコマンドラインを取得するにはCommand関数を使用する。Command関数はCプログラマにはおなじみのargc、argvと異なり、プログラムを起動したコマンド名は含まれず、全てのパラメータを含む単一の文字列として返す。

 取得したコマンドラインをスペースで区切るにはSplit関数を利用すると良い。

' コマンドラインを取得しスペースで分割します
Function getArgument() As String()
    Dim strArg() As String
    Dim i As Long
    
    strArg = Split(Command())                   ' スペースで分割
    
Debug.Print "UBound(strArg) = " & UBound(strArg)
    For i = 0 To UBound(strArg)
Debug.Print strArg(i)
    Next i
    
    getArgument = strArg
End Function

コマンドラインを取得するサンプル

[VB]コンソールアプリケーションの起動と完了待ち

 VBから他アプリケーションを起動するにはShell関数を使う方法とAPIのCreateProcessを使う方法がある。私は主に後者を用いる。

 起動したアプリケーションの完了を待つにはそのプロセスのハンドルを取得し、APIのWaitForSingleObjectで待機する。取得したハンドルは必要がなくなったら解放する。

<Shell関数を使う方法>

    Dim pid As Long
    Dim ph As Long
    Dim ecode As Long

    pid = Shell("aaa.exe work.txt", vbNormalFocus) ' aaa.exe実行 work.txtは引数
    ph = OpenProcess(SYNCHRONIZE Or PROCESS_QUERY_INFORMATION, True, pid)
    WaitForSingleObject ph, 100000 ' aaa.exeが終了するまで待つ

    GetExitCodeProcess ph, ecode  ' 終了コード取得
    CloseHandle ph ' プロセスハンドルを閉じる

<CreateProcessを使う方法>

    Dim pInfo As PROCESS_INFORMATION
    Dim sInfo As STARTUPINFO
    Dim lngRet As Long
    Dim ecode As Long

    sInfo.cb = LenB(sInfo)

    lngRet = CreateProcess(vbNullString, _
        "aaa.exe work.txt", _
        ByVal 0&, _
        ByVal 0&, _
        1&, _
        NORMAL_PRIORITY_CLASS, _
        ByVal 0&, _
        vbNullString, _
        sInfo, _
        pInfo)

    WaitForSingleObject pInfo.hProcess, 100000 ' aaa.exeが終了するまで待つ

    GetExitCodeProcess pInfo.hProcess, ecode  ' 終了コード取得
    CloseHandle pInfo.hThread ' スレッドハンドルを閉じる
    CloseHandle pInfo.hProcess ' プロセスハンドルを閉じる

 何れの方法も出力のリダイレクトができないようです。リダイレクトを必要とする場合は、予めバッチファイルを作成し、そのバッチファイルを実行すると良い。またShell関数またはCreateProcessを呼び出した後は、DoEvents等でCPUを解放すると良いようだ。

 APIのOpenProcessの第一引数はNT系でのみ有効である。APIのGetExitCodeProcessにより、起動したアプリケーションの終了コードを取得し、処理を分岐させることが可能である。

コンソールアプリケーションの起動と完了待ちのサンプル

[VB]Excelの起動

 VBで作成するアプリケーションからExcelを起動することがでる。もちろんExcelがインストールされている必要がある。Excelは巨大なActiveXオブジェクトなので、前述の通常のアプリケーションの起動方法とは異なるアプローチを用いる。こちらはオートシェイプのサンプルを見て欲しい。

    Dim objAppXL As Excel.Application

    Set objAppXL = CreateObject("Excel.Application") ' Excel
    objAppXL.Caption = "サンプル"
    objAppXL.Visible = True ' 表示

 このプログラムの動作にはExcelのタイプライプラリもインポートする必要があるのでお忘れなく。Excelのオブジェクトを作成することができれば、後はVBAと同じ感覚で操作することができる。

[VB]円形のスクリーンを描画する

 リージョンかあ...大変だったなぁ。リージョンを使うとVisual Basicでも多様なグラフィック処理が可能になる。

 正方形のリージョンを丸く刳り抜き、型枠を作成して上から被せる事により、円形のスクリーンを描くことが可能になる。この処理の肝はAPIのCombineRgnと、ピクチャーボックスのFillStyleプロパティを動的に変更することにある。画面更新の度にスクリーンを消去するが、AutoRedrawプロパティをTrueに設定しておく事でウィンドウのちらつきは最小限に抑えることができる。

    GetClipBox pctScreen.hdc, mRect             ' スクリーンのサイズを取得
                                                ' 円形のリージョンを作成
    hRgn2 = CreateEllipticRgn(mRect.Left - 1, mRect.Top - 1, mRect.Right + 2, mRect.Bottom + 2)
    mhRgn1 = CreateRectRgnIndirect(mRect)       ' 正方形のリージョンを作成(スクリーンのサイズ)
    CombineRgn mhRgn1, mhRgn1, hRgn2, RGN_XOR   ' 真中を丸くくりぬいたリージョンを作成
    DeleteObject hRgn2                          ' 不要なリージョンは解放

    pctScreen.FillStyle = vbFSSolid         ' 塗り潰し
    PaintRgn pctScreen.hdc, mhRgn1          ' 型枠の描画
    pctScreen.FillStyle = vbFSTransparent   ' 透明

円形スクリーンを描画するサンプル

[VB]App.Path

 当然ご存知とは思うが念のため。App.Pathは実行ファイルのあるディレクトリのパスが入っている。例えば、
    strFullPath = App.Path & "\test.txt"
 と書くと、実行ファイルと同一のディレクトリにある「test.txt」のパスを生成できる。

[VB]高速なファイルの読み込み

 ファイルを読み込むときはLine Inputが便利であるが、高機能な分遅い。通常の使用には全く問題がないが、大量に生成されるログを読み込む等の処理は難しい。そこで僕はこのような方法を使っている。
    bytSjis = InputB(lngReadSize, #1)       ' SJISのテキスト読み込み

    strUni = StrConv(bytSjis, vbUnicode)    ' SJISからUNICODEへ
 まず、InputBで必要なサイズ分ファイルから読み込む。InputBが返すのはバイトデータであるので、Visual Basicでテキストとして表示するためにはStrConvでUNICODEに変換する必要がある。例えば、これを100ms周期のファイルの差分読み込みに使用すると、Line Inputではすぐに動かなくなる処理が、ほとんどCPU時間を消費せずに可能になる。

高速なファイル読み込みのサンプル

[VB,VBA]オートシェイプを描画する

 VBアプリケーションからExcelのオートシェイプを描画することができる。これはそれほど難しくないのだが、オートシェイプの操作方法を書いた資料がほとんど見当たらず、少しずつコードを書いては動かして調べた。

 VBAはもちろんのこと、COMを使用してVBからもオートシェイプを操作することができる。VBでは「参照設定」でMicrosoft Excel x.x Object LibraryとMicrosoft Office x.x Object Libraryを参照するのを忘れてはならない。x.xの部分はバージョンによって異なる。

    With ActiveSheet.Shapes.AddShape(msoShapeFlowchartProcess, _
            20, 20, 100, 20).TextFrame
        .Characters.Text = "Allegy"             ' オートシェイプの中に文字
        .Characters.Font.Size = 10              ' フォントサイズを10ポイントに
        .HorizontalAlignment = xlHAlignCenter   ' 中央揃え
        .VerticalAlignment = xlVAlignCenter     ' 中央揃え
    End With

オートシェイプを描画するサンプル(Excelファイル)

Excelを起動し、オートシェイプを描画するサンプル

[VBA]標準メニューに項目を追加する

 Excel97からメニューの扱いが変わり多少苦労した。ここではブックのオープン時に、標準メニューに項目を追加する方法を紹介する。まずVisualBasicEditorを開き、ThisWorkbookのWorkbook_Openに以下のようなコードを記述する。

Private Sub Workbook_Open()
    With Application.CommandBars("Worksheet Menu Bar")
        
        ' メニューに項目を追加
        With .Controls.Add(Type:=msoControlPopup, Temporary:=True)
            .Caption = "サンプル(&S)"
            ' サブメニューAAAを追加
            .Controls.Add Type:=msoControlButton
            With .Controls(1)
                .Caption = "AAA(&A)"
                .OnAction = "mnuAaa" ' AAA選択時に実行されるSubプロシージャ名
            End With
            ' サブメニューBBBを追加
            .Controls.Add Type:=msoControlButton
            With .Controls(2)
                .Caption = "BBB(&B)"
                .OnAction = "mnuBbb" ' BBB選択時に実行されるSubプロシージャ名
            End With
        End With
    End With
End Sub

 OnActionプロパティにより、メニュー選択時のSubプロシージャと結び付ける。結びつけるSubプロシージャは標準モジュール内に記述する。

Sub mnuAaa()
    MsgBox "AAA"
End Sub

Sub mnuBbb()
    MsgBox "BBB"
End Sub

 このExcelファイルを保存して終了し、再び開くと標準メニューの最後に「サンプル」と言う名前のメニュー項目が追加されているはずである。

 .Controls.AddするときにTemporary:=Trueを指定しているため、このExcelファイルを開いていないときはメニュー項目に「サンプル」は現れない。XLStartフォルダにこのExcelファイルを入れることで、Excelの起動時に常に「サンプル」メニューを表示することができる。

標準メニューに項目を追加するサンプル(Excelファイル)

[VBA]画面更新を抑止して高速化

 Excelの機能を利用して膨大な数のグラフィックを描くと非常に時間がかかる。そんな時ScreenUpdatingプロパティを変更して画面更新を抑止すると、かなり時間の節約になる。グラフィックの描画が終わったときはScreenUpdatingプロパティを元に戻す必要があるさもないと永久に画面の更新はされない。
    Application.ScreenUpdating = False

[VBA]数式を設定する

 プログラムからセルに数式を設定して、Excelの機能を使って例えば合計を求めたりすることがでる。もちろん、Formulaプロパティに文字列として数式を設定しているだけなので、合計だけでなくさまざまな数式を入力することができる。
    Range("A6").Formula = "=SUM(A1:A5)"

 さらにセルアドレスも併用すると、ループ処理などで利用できるようになる。AddressプロパティはExcelではおなじみの"A1"とか"B3"等の文字列を返します。

    Cells(6, 3).Formula = "=SUM(" & Cells(1, 3).Address(False, False) & ":" & Cells(5, 3).Address(False, False) & ")"

数式を設定するサンプル(Excelファイル)

[VBA]ソートを行う

 ソートは任意のRangeに対してSortメソッドを使用する。キーは複数設定可能で、また昇順及び降順の何れも実行可能だ。
    Range("A1:B10").Sort Key1:=Columns("A")

ソートを行うサンプル(Excelファイル)

[VBA]検索を行う

 検索は任意のRangeに対してFindメソッドを使用する。セルが検索された場合、そのセルが返されるのでRangeオブジェクトにSetする。セルが検索されなかった場合、Nothingが返される。
    Dim objRange As Range
    
    Set objRange = Columns("A").Find("DEF")

検索を行うサンプル(Excelファイル)

[VBA]ThisWorkbook.Path

 VBのApp.PathみたいなものがVBAにもある。ThisWorkbook.Pathがそれである。ちなみにApplication.Pathは、excel.exeがあるディレクトリを知ることができる。

Copyright(C) 2001-2003 Allergy Design Office All rights reserved.

[Home]