SSブログ

トロPのVBAホビラボ Stringを調べてみる [トロPのVBAホビラボ]

前回はメモリーイメージを調査する際に使う、簡単なユーティリティーを準備しました(→ご参照)。と、同時に単に使い方をみるためだけのサンプルプログラムを提示して終わりましたが、
せっかく作ったので今回は少し使ってみましょう。

今回は、String型のメモリーイメージです。

1点お断りしておきますが、今後も基本的に32ビット環境で実施していきます。
(理由は主旨から外れますのでここでは控えたいと思います。ご容赦を。)

さて、話を戻しましょう。

String型の変数の場合、VBAには変数のアドレスを返す関数が2つあります。

  • VarPtr
  • StrPtr

1つの変数に2種類のアドレス???

私は即座に違和感がありましたが、みなさんはいかがでしょうか。この疑問は言い換えればこの2つのどちらを使えばいいの?使いどころは?ということです。

では実際にメモリーイメージを見てみましょう。

サンプル
Public Sub StringMemory()

    Dim l_AString    As String
    Dim l_AStringPos As Long
    Dim l_BSTRLenPfx As Long
    Dim l_ChrCodes   As String

    l_AString = "私は文字列A。"

    Debug.Print "AString: " & l_AString

    Debug.Print
    Debug.Print "[VBA Information]"

    Debug.Print "VarPtr: " & Hex(VarPtr(l_AString))
    Debug.Print "StrPtr: " & Hex(StrPtr(l_AString))
    Debug.Print "Len: " & Len(l_AString)
    Debug.Print "LenB: " & LenB(l_AString)

    l_ChrCodes = ""
    For l_AStringPos = 1 To Len(l_AString)
        If 0 < Len(l_ChrCodes) Then l_ChrCodes = l_ChrCodes & ", "
        l_ChrCodes = l_ChrCodes & Hex(AscW(Mid(l_AString, l_AStringPos, 1)))
    Next l_AStringPos
    Debug.Print "Character codes: " & l_ChrCodes

    Debug.Print
    Debug.Print "[As BSTR]"

    ModUtility.CopyMemory VarPtr(l_BSTRLenPfx), StrPtr(l_AString) - 4, 4
    Debug.Print "Length Prefix: " & l_BSTRLenPfx

    Debug.Print "--- Data string ---"
    ModUtility.DisplayMemory StrPtr(l_AString), l_BSTRLenPfx

    Debug.Print "--- Terminator ---"
    ModUtility.DisplayMemory StrPtr(l_AString) + l_BSTRLenPfx, 2

End Sub

イミディエイトウィンドウ
AString: 私は文字列A。

[VBA Information]
VarPtr: 1FF490
StrPtr: 1537345C
Len: 7
LenB: 14
Character codes: 79C1, 306F, 6587, 5B57, 5217, 41, 3002

[As BSTR]
Length Prefix: 14
--- Data string ---
1537345C C1 79 6F 30 87 65 57 5B 17 52 41 00 02 30
--- Terminator ---
1537346A 00 00

わかりにくいので、上の結果を少し図示してみました。

VBA Stringのメモリーイメージ

VarPtrとStrPtrのアドレスがどういう位置なのかわかりました。

(1) BSTRデータへのポインター(VarPtr値を格納する領域)4バイト(BSTR外)
(2) データサイズが格納する領域(データ本体からの相対位置-4から始まる4バイト、BSTR内)
(3) データ本体を格納する領域(可変長、BSTR内)
(4) データ終端文字を格納する領域(2バイト)

また、Stringが文字データの他若干10バイトほどの領域が必要というようなことがオンラインヘルプに書かれていたことがあったかと思いますが、どうやらその正体は上の(1)(2)(4)ですね。まあ、このことは広大なメモリーを搭載している現在のパソコンでは何ら問題になりませんが。

さて、ここで次のプログラムを実行するとイミディエイトウィンドウには何と表示されるでしょうか。

サンプル
Public Sub NullWithinString()

    Dim l_AString As String

    l_AString = "A" & vbNullChar & "B"

    Debug.Print Len(l_AString)

End Sub

C言語に慣れておられる方には違和感があるでしょうが、実は「3」と表示されます。
もう1回メモリーイメージの結果を思い出してみましょう。

確かに(4)で終端にヌル文字を持っていますので、これを使って文字列の終端判別をしそうなものです。ですが、ならなぜ(2)でデータ本体のサイズを持っているのでしょうか。

これは読みやすさのためというより、このように任意の文字を含めることができるような構造にするためなのでしょう。

実は、ちょくちょくBSTRという謎めいた記述にお気づきかと思います。

少し脇道ですが、VBA(ないし古いVB)は、COMオブジェクトを使いやすくした抽象データを
定義できる言語ということができるでしょう。COMオブジェクトというのは参照設定してつかうあれです。

COMでは文字列をBSTR型で扱うことをご存知の方は、正解できたのではないでしょうか。

このようにVBAのString型はCOMとの親和性のある構造になっていますが、一方そのために
Windows APIを使うときに違和感を生じる原因にもなっているのではなかろうかと思います。

次回はAPIとの関連について調べてみましょうか。

今日の結論
VBAのStringはBSTR実体+BSTRへのポインター。

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

nice! 0

コメント 0

コメントを書く

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

トラックバック 0

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

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