● メモリマップドファイルを嗜んでみる ●

ダウンロード (26KB)

メモリマップドファイルはどちらかと言うと、共有メモリ的な使い方をされることが多そうだけど、ここではそのような使い方をしていない。もう1つの使い方、即ちファイルデータをメモリに展開し、文字列のポインタとしてデータを扱う試みを実験的に行っている。それが上記のサンプルに詰まっているはずである

またサンプルにはVBではまったくなじみがないと思われる strstr が使用されている。これに関する話題と言うかネタはVBでstrstrを使用して文字列検索するを参照されたい。

メモリマップドファイルによるメモリ(文字列ポインタ)の取得方法は極めて簡単である。
以下にコードを載せてみる。API関数や定数はサンプルのソースを確認してね。

'ファイルハンドル
Private m_hFile As Long

'メモリマップドファイルのハンドル
Private m_hMapp As Long

'ファイル内容(文字列)のポインタ
Private m_lpData As Long

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:ファイルを開いて、マッピングオブジェクトを作成する
' 引  数:(i)FileName   … ファイル名
'         (i)IsReadOnly … 読み取り専用にする場合はTrue
' 返り値:正常…True  異常…False
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Private Function OpenFile(ByVal FileName As String, _
                          Optional ByVal IsReadOnly As Boolean = False) As Boolean
    'ファイルを開く
    m_hFile = CreateFile(FileName, GENERIC_READ Or IIf(IsReadOnly, 0, GENERIC_WRITE), 0, 0, OPEN_EXISTING, 0, 0)
    If m_hFile = INVALID_HANDLE_VALUE Then Exit Function

    'メモリマップドファイルを作成する
    If IsReadOnly Then
        m_hMapp = CreateFileMapping(m_hFile, 0, PAGE_READONLY, 0, 0, 0&)
    Else
        m_hMapp = CreateFileMapping(m_hFile, 0, PAGE_READWRITE, 0, 0, 0&)
    End If
    If m_hMapp = 0 Then Exit Function

    'ファイル内容(文字列)のポインタを取得
    m_lpData = MapViewOfFile(m_hMapp, IIf(IsReadOnly, FILE_MAP_READ, FILE_MAP_WRITE), 0, 0, 0)
    If m_lpData = 0 Then Exit Function

    '正常終了
    OpenFile = True
    Exit Function

ErrHandler:
    'ファイルを閉じる
    Call CloseFile
End Function

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:ファイルを閉じる
' 引  数:なし
' 返り値:なし
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Private Sub CloseFile()
    'ファイル内容(文字列)のポインタを開放する
    If m_lpData Then
        Call UnmapViewOfFile(m_lpData)
        m_lpData = 0
    End If

    'メモリマップドファイルのハンドルを破棄する
    If m_hMapp Then
        Call CloseHandle(m_hMapp)
        m_hMapp = 0
    End If

    'ファイルを閉じる
    If m_hFile Then
        Call CloseHandle(m_hFile)
        m_hFile = 0
    End If
End Sub

OpenFile を実行し正常に動作すると、m_lpData変数にファイルデータのポインタが設定される。m_lpDataをあれこれ操作してこれ以上行う処理が無くなったら CloseFile を実行して、色々なものを閉じる。基本的な処理はこれだけで本当に簡単。

ファイルデータはメモリに展開され、そのメモリの位置、要するにポインタがm_lpData変数に格納される。ということは、C言語的文字列関数でデータを色々と操作できるわけである。strlen(m_lpData) とすれば文字列のバイト数(=ファイルサイズ)を取得できるし、strncpy(m_lpData + 3, "ABC", 3) とすれば先頭4バイト目から6バイト目をABCに書き換えられる(但し7バイト目にはNULL文字が設定される)し、FillMemory(m_lpData + 10, 10, Asc("A")) とすれば11バイト目から20バイト目をAに書き換えられる。処理も幾分速そうである。

上記のような書き方をすると、テキストファイルしか扱えないと思われるが、そんなことは無いのでご安心をば。メモリマップドファイルはNULL文字までしか読まないということは無くファイル全体を読んでくれるので、バイナリファイルも余裕で扱える。この場合、構造体などのデータのやり取りは strcpy や strlen のような文字列関数ではなく、CopyMemory を使用して行う。取り立てて難しいことはない。

メモリマップドファイルを読み書きモード(PAGE_READWRITE)で作成している場合は、書き換えた内容を Call FlushViewOfFile(m_lpData, 0) とすることで即座にファイル出力できる。メモリサイズを動的に変更してファイル出力できるかは調査不足で不明であるが、メモリサイズを変えないデータ変更処理が必要な場合は極めて有用である。じゃあそれってどんな場合か、と考えてみたもののそれほど多くは思い浮かばなかったが、大文字→小文字変換、ビットマップの色変換などで使用できそうである。

この方法を知ってしまったら、テキストファイルを開いて、その内容を String 変数に設定して何らかの処理を行う、という今までやってきた作業がアホらしくなるかもしれない。それはともかく、いつでも使えるようにクラス化しておくと良いかもしれないですな。


戻る