SSブログ

トロPのVBAホビラボ メモリーイメージ調査準備 [トロPのVBAホビラボ]

VBAを比較的使い込んでくると、今までより進んだことがしたいと思うときがあります。例えば、細かいこだわりをきかせたり、MS-Officeのアプリケーションの範囲を超えることをしたりというとき。

そんなときの解決法として、Web上ではWindows APIを使用する方法が紹介されています。Windows APIを使うという、言ってみれば大それたことをするわけです。当然のことながらより広い知識が必要となります。

VBAでどのWindows APIを使うときにも共通して、次の3つのことを知る必要があります。

(1) どのような引数をとり、どのような値を戻し、どういう名前のもので、それがどのファイルに定義されているか

(2) 引数や戻り値の型をVBAではどのように宣言すればよいか

(3) 引数がどのように渡り、戻り値がどのように格納されるか

(異世界のものにアクセスしようというのですから、本来はバイナリーインターフェイスについて知らなければなりませんが、これからWindows APIを使おうという場合は、当面そこまで踏み込む必要はありません。そこまで踏み込まなければならないときは、こだわりを捨てる方がたいてい賢明です。)

(1)、(2)はWeb上で検索すると多くの情報が得られますし、ある程度の規則性も見いだせることでしょう。(実はあまり具体的な型は関係なくて、サイズが問題だったりします。)

(3)はAPIによりかなり難易度が変わりますが、基本的にはメモリーイメージを思い描けることが必要です。C言語でポインターを扱う経験がある方は、この辺得意分野ですね。

メモリーイメージを表示するツールを使って調べるというのも手ですが、こんどはそのツールに習熟する必要があります。そのかわりに、次のような標準モジュールを作成しておくと、VBAの世界の中で済み、メモリーへの基本的なアクセス方法のメモ書きも兼ねていて、便利です。

今後の手始めに用意しておくとよいでしょう。

ModUtility
Option Explicit

Private Declare PtrSafe Sub WAPI_CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (ByVal Destination As LongPtr, ByVal Source As LongPtr, _
    ByVal Length As LongPtr)

Public Sub CopyMemory _
(ByVal DestinationAddress As LongPtr, ByVal SourceAddress As LongPtr, _
ByVal LengthInBytes As LongPtr)

    WAPI_CopyMemory DestinationAddress, SourceAddress, LengthInBytes

End Sub

Public Sub DisplayMemory _
(ByVal StartAddress As LongPtr, ByVal LengthInBytes As LongPtr)

    Dim l_AddrDisp     As String
    Dim l_AddrDispLen  As Long
    Dim l_Buf(1 To 16) As Byte
    Dim l_BufPos       As Long
    Dim l_BufStartAddr As LongPtr
    Dim l_ByteDisp     As String
    Dim l_CurAddr      As LongPtr
    Dim l_EndAddr      As LongPtr
    Dim l_LineDisp     As String
    Dim l_ValidBufLen  As LongPtr

    l_EndAddr = StartAddress + LengthInBytes - 1
    l_BufStartAddr = VarPtr(l_Buf(1))

    For l_CurAddr = StartAddress To l_EndAddr Step 16
        If (l_EndAddr - l_CurAddr + 1) < 16 Then
            l_ValidBufLen = (l_EndAddr - l_CurAddr + 1)
        Else
            l_ValidBufLen = 16
        End If
        ModUtility.CopyMemory l_BufStartAddr, l_CurAddr, l_ValidBufLen
        l_AddrDisp = Hex(l_CurAddr)
        l_AddrDispLen = 2 * LenB(l_CurAddr)
        Do While Len(l_AddrDisp) < l_AddrDispLen
            l_AddrDisp = "0" & l_AddrDisp
        Loop
        l_LineDisp = l_AddrDisp
        For l_BufPos = 1 To l_ValidBufLen
            If l_Buf(l_BufPos) < &H10 Then
                l_ByteDisp = "0" & Hex(l_Buf(l_BufPos))
            Else
                l_ByteDisp = Hex(l_Buf(l_BufPos))
            End If
            l_LineDisp = l_LineDisp & " " & l_ByteDisp
        Next l_BufPos
        Debug.Print l_LineDisp
    Next l_CurAddr

End Sub

DisplayMemoryを呼び出すと、イミディエイトウィンドウにメモリーイメージを書き出します。VBAにはポインターの概念はありませんので、指定アドレスのデータをバッファーにコピーして、そのバッファーのデータを表示するようにしています。

データのコピーもよく使いますので、CopyMemoryの方もPublicにしておきます。

調査したいプログラムの中で直接DisplayMemoryを呼び出したり、ストップ中にイミディエイトウィンドウでDisplayMemoryを実行したりして使います。

試しに、下記のプロシージャーを実行してみてください。

サンプル
Public Sub Proc1()

    Dim l_ALongPtr As LongPtr

    l_ALongPtr = 1

    ModUtility.DisplayMemory VarPtr(l_ALongPtr), LenB(l_ALongPtr)

End Sub
32ビット環境でのイミディエイトウィンドウへの出力例
0047F4B0 01 00 00 00

32ビット環境ですので、LongPtrのサイズは4バイトです。

実際にはアドレス0047F4B0に、想定通り「01 00 00 00」という4バイトのデータが格納されているのがわかりました。

Windowsの世界は「リトルエンディアン」という下位バイトから順番に格納される方式になっていますので、このようになっています。

では、今日はこのへんで。


広告
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。