トロ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の世界は「リトルエンディアン」という下位バイトから順番に格納される方式になっていますので、このようになっています。
では、今日はこのへんで。
コメント 0