Results 1 to 11 of 11

Thread: VB6 - Treat a file like an array without reading to memory.

Threaded View

  1. #1

    Thread Starter
    Fanatic Member Comintern's Avatar
    Join Date
    Nov 2004
    Location
    Lincoln, NE
    Posts
    826

    VB6 - Treat a file like an array without reading to memory.

    Here's a little snippet of code I posted over on CodeGuru that I thought people over here might be able to make use of. It creates a memory map of a file, meaning that instead of loading the file into memory, it creates a pointer to the file's physical location on the disk. So, you can treat the file as a variable, and any changes you make to the variables are also changing the target file. While useful in and of itself, it's a little limiting without a nice convienent way to index into it (are you thinking array? I am).

    The following sample code opens a memory mapped file, creates a byte array, and then modifies the data pointer in the array to point at the file. So, you can do 'reads' with bVar = bByte(x), and 'writes' with bByte(x) = bVar.

    It wouldn't be hard to convert from a byte array to another type of array (string excluded, unless the strings were fixed length). All you need to do in that case would be to change the cbElements of the array structure to the size of the chunk of data you want--i.e., if you wanted to treat the file as an array of Longs, cbElements would be 4, a UDT would be LenB(uType), etc. The second type (SafeBound) holds the LBound and UBound information. I wouldn't change the LBound, but you'll want to manually calculate the UBound based on the length of the file and the size of the elements. So, if the file was 1000 bytes and you wanted to treat it as an array of Longs, you would set uTemp.cbElements to 4 and set uTemp.rgsabound.cElements to 249.

    Couple of things you might want to do would be to add error checking for the API calls and re-build the original array (mainly just point it back to what it was pointing to before you hacked it) after you remove the memory map to make sure that the memory for the original array data gets de-allocated when it's garbage collected.

    Here's the code. This example opens a file and changes the first byte to "=".
    VB Code:
    1. 'API DECs
    2. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, _
    3.                                                                      ByVal ByteLen As Long)
    4. Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, _
    5.                                          ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, _
    6.                                          ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, _
    7.                                          ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
    8. Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    9.  
    10. 'MEMORY MAPPING APIs
    11. Private Declare Function MapViewOfFile Lib "kernel32.dll" (ByVal hFile As Long, ByVal dwDesiredAccess As Long, _
    12.                                          ByVal dwFileOffsetHigh As Long, ByVal dwFileOffsetLow As Long, _
    13.                                          ByVal dwNumberOfBytesToMap As Long) As Long
    14. Private Declare Function CreateFileMapping Lib "kernel32.dll" Alias "CreateFileMappingA" (ByVal hFile As Long, _
    15.                                          ByVal lpAttributes As Long, ByVal flProtect As Long, _
    16.                                          ByVal dwMaximumSizeHigh As Long, ByVal dwMaximumSizeLow As Long, _
    17.                                          ByVal lpName As String) As Long
    18. Private Declare Function UnmapViewOfFile Lib "kernel32.dll" (ByVal lpBaseAddress As Long) As Boolean
    19.  
    20. 'STRUCTS FOR THE SAFEARRAY:
    21. Private Type SafeBound
    22.     cElements As Long
    23.     lLbound As Long
    24. End Type
    25.  
    26. Private Type SafeArray
    27.     cDim As Integer
    28.     fFeature As Integer
    29.     cbElements As Long
    30.     cLocks As Long
    31.     pvData As Long
    32.     rgsabound As SafeBound
    33. End Type
    34.  
    35. 'MISC CONSTs
    36. Private Const VT_BY_REF = &H4000&
    37. Private Const FILE_SHARE_READ = &H1
    38. Private Const FILE_SHARE_WRITE = &H2
    39. Private Const MOVEFILE_REPLACE_EXISTING = &H1
    40. Private Const FILE_ATTRIBUTE_TEMPORARY = &H100
    41. Private Const FILE_ATTRIBUTE_NORMAL = &H80
    42. Private Const FILE_BEGIN = 0
    43. Private Const CREATE_NEW = 1
    44. Private Const OPEN_EXISTING = 3
    45. Private Const OPEN_ALWAYS = 4
    46. Private Const GENERIC_READ = &H80000000
    47. Private Const GENERIC_WRITE = &H40000000
    48. Private Const PAGE_READWRITE = 4
    49. Private Const FILE_MAP_WRITE = &H2
    50. Private Const FILE_MAP_READ = &H4
    51. Private Const FADF_FIXEDSIZE = &H10
    52.  
    53. Private Sub MapFileMemory()
    54.  
    55.     Dim hFile As Long, sFile As String, lPointer As Long, hFileMap As Long, lFileLen As Long, uTemp As SafeArray
    56.     Dim bBytes() As Byte
    57.  
    58.     sFile = "D:\Test2.txt"                              'Set the filename.
    59.                                                         'Map it.
    60.     hFile = CreateFile(sFile, GENERIC_READ Or GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
    61.     hFileMap = CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, 0, "MySharedMapping")
    62.     lPointer = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0)
    63.  
    64.     bBytes = StrConv("TestTestTest", vbFromUnicode)     'Create an array.
    65.    
    66.     lFileLen = FileLen(sFile)                           'Find the length of the target file.
    67.    
    68.     If GetArrayInfo(bBytes, uTemp) Then                 'Load the UDT with the array info.
    69.         uTemp.cbElements = 1                            'Set element size to a byte.
    70.         uTemp.rgsabound.cElements = lFileLen            'Set the UBound of the array.
    71.         uTemp.fFeature = uTemp.fFeature And FADF_FIXEDSIZE  'Set the "Fixed size" flag, SHOULD MAKE REDIM FAIL!
    72.         uTemp.pvData = lPointer                         'Point it to the memory mapped file as it's data.
    73.         Call AlterArray(bBytes, uTemp)                  'Write the UDT over the old array.
    74.     End If
    75.  
    76.     For lFileLen = LBound(bBytes) To UBound(bBytes)     'This should be reading from the file.
    77.         Debug.Print bBytes(lFileLen)
    78.     Next lFileLen
    79.  
    80.     bBytes(0) = 61                                      'Change the first char to a "=" to see if it worked.
    81.    
    82.     UnmapViewOfFile lPointer                            'Release the memory map.
    83.     CloseHandle hFile                                   'Close the opened file.
    84.  
    85. End Sub
    86.  
    87. Private Function GetArrayInfo(vArray As Variant, uInfo As SafeArray) As Boolean
    88.    
    89.     'NOTE, the array is passed as a variant so we can get it's absolute memory address.  This function
    90.     'loads a copy of the SafeArray structure into the UDT.
    91.    
    92.     Dim lPointer As Long, iVType As Integer
    93.    
    94.     If Not IsArray(vArray) Then Exit Function               'Need to work with a safearray here.
    95.  
    96.     With uInfo
    97.         CopyMemory iVType, vArray, 2                        'First 2 bytes are the subtype.
    98.         CopyMemory lPointer, ByVal VarPtr(vArray) + 8, 4    'Get the pointer.
    99.  
    100.         If (iVType And VT_BY_REF) <> 0 Then                 'Test for subtype "pointer"
    101.             CopyMemory lPointer, ByVal lPointer, 4          'Get the real address.
    102.         End If
    103.        
    104.         CopyMemory uInfo.cDim, ByVal lPointer, 16           'Write the safearray to the passed UDT.
    105.        
    106.         If uInfo.cDim = 1 Then                              'Can't do multi-dimensional
    107.             CopyMemory .rgsabound, ByVal lPointer + 16, LenB(.rgsabound)
    108.             GetArrayInfo = True
    109.         End If
    110.     End With
    111.  
    112. End Function
    113.  
    114. Private Function AlterArray(vArray As Variant, uInfo As SafeArray) As Boolean
    115.    
    116.     'NOTE, the array is passed as a variant so we can get it's absolute memory address.  This function
    117.     'writes the SafeArray UDT information into the actual memory address of the passed array.
    118.    
    119.     Dim lPointer As Long, iVType As Integer
    120.  
    121.     If Not IsArray(vArray) Then Exit Function
    122.  
    123.     With uInfo
    124.         CopyMemory iVType, vArray, 2                        'Get the variant subtype
    125.         CopyMemory lPointer, ByVal VarPtr(vArray) + 8, 4    'Get the pointer.
    126.  
    127.         If (iVType And VT_BY_REF) <> 0 Then                 'Test for subtype "pointer"
    128.             CopyMemory lPointer, ByVal lPointer, 4          'Get the real address.
    129.         End If
    130.  
    131.         CopyMemory ByVal lPointer, uInfo.cDim, 16           'Overwrite the array with the UDT.
    132.  
    133.         If uInfo.cDim = 1 Then                              'Multi-dimensions might wipe out other memory.
    134.             CopyMemory ByVal lPointer + 16, .rgsabound, LenB(.rgsabound)
    135.             AlterArray = True
    136.         End If
    137.  
    138.     End With
    139.  
    140. End Function
    Last edited by Comintern; May 8th, 2006 at 08:37 AM. Reason: Added "VB6" to the title.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width