I was recently asked come up with a process for generating MD5 Hash values for a list of files. That wasn't too difficult but problems arose when the file size was larger than about 200M. I found some code here to work around that until the file size exceeded 2G.

After a pretty extensive Google search and some modifications to a couple of seemingly complete class modules found on Stack Overflow, I have come up with a process that appears to handle very large files (at least a large as the largest file I have had to process > 3G).

The problem is that classes were apparently written around 2005 and all of the declarations are 32 bit. I was hoping that some guy or gal here with a 64 bit installation of Excel would take interest and see if they can modify the conditional If statements so the code will function in both 64 and 32 bit applications. Here is the code in three sections

Here is the file: Declarations Need Help.xlsmDeclarations Need Help.xlsm

1. The calling procedure. Put it in any standard code module:

Private Sub TestHash()
Dim Hash As clsMD5Hash
Dim strFilePath As String
   strFilePath = "D:\Test\A File.docm" 'Any valid file path
   Set Hash = New clsMD5Hash
   ActiveWorkbook.ActiveSheet.Range("A1").Value = Hash.HashFile(strFilePath)
lbl_Exit:
  Exit Sub
End Sub
lbl_Exit:
  Exit Sub
End Sub
Declarations Need Help.xlsm
2. The Hash Class. Put it in a class module named clsMD5Hash:
Option Explicit
'Add all of this to a class named "clsMD5Hash"  (Very large binary file)
Private Const ALG_TYPE_ANY          As Long = 0
Private Const ALG_CLASS_HASH        As Long = 32768
Private Const ALG_SID_MD5           As Long = 3
Private Const CALG_MD5              As Long = ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_MD5
Private Const PROV_RSA_FULL         As Long = 1
Private Const CRYPT_VERIFYCONTEXT   As Long = &HF0000000
Private Const MS_DEFAULT_PROVIDER   As String = "Microsoft Base Cryptographic Provider v1.0"
Private Const HP_HASHVAL            As Long = 2
Private Const HP_HASHSIZE           As Long = 4
#If VBA7 Then
Private Declare Function CryptAcquireContext Lib "advapi32" Alias "CryptAcquireContextA" ( _
                            ByRef phProv As Long, ByVal pszContainer As String, _
                            ByVal pszProvider As String, ByVal dwProvType As Long, _
                            ByVal dwFlags As Long) As Long
Private Declare Function CryptCreateHash Lib "advapi32" (ByVal hProv As Long, ByVal algid As Long, _
                            ByVal hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long
Private Declare Function CryptDestroyHash Lib "advapi32" (ByVal hHash As Long) As Long
Private Declare Function CryptGetHashParam Lib "advapi32" (ByVal hHash As Long, ByVal dwParam As Long, _
                            ByRef pbData As Any, ByRef pdwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptHashData Lib "advapi32" (ByVal hHash As Long, ByRef pbData As Any, _
                            ByVal dwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptReleaseContext Lib "advapi32" (ByVal hProv As Long, ByVal dwFlags As Long) As Long
Private m_hHash As Long
Private m_hProvider As Long
#Else
Private Declare Function CryptAcquireContext Lib "advapi32" Alias "CryptAcquireContextA" ( _
                            ByRef phProv As Long, ByVal pszContainer As String, _
                            ByVal pszProvider As String, ByVal dwProvType As Long, _
                            ByVal dwFlags As Long) As Long
Private Declare Function CryptCreateHash Lib "advapi32" (ByVal hProv As Long, ByVal algid As Long, _
                            ByVal hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long
Private Declare Function CryptDestroyHash Lib "advapi32" (ByVal hHash As Long) As Long
Private Declare Function CryptGetHashParam Lib "advapi32" (ByVal hHash As Long, ByVal dwParam As Long, _
                            ByRef pbData As Any, ByRef pdwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptHashData Lib "advapi32" (ByVal hHash As Long, ByRef pbData As Any, _
                            ByVal dwDataLen As Long, ByVal dwFlags As Long) As Long
Private Declare Function CryptReleaseContext Lib "advapi32" (ByVal hProv As Long, ByVal dwFlags As Long) As Long
Private m_hHash As Long
Private m_hProvider As Long
#End If
Private Sub Class_Initialize()
  If CryptAcquireContext(m_hProvider, vbNullString, MS_DEFAULT_PROVIDER, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) = 0 Then
    Err.Raise vbObjectError Or &HC352&, "MD5Hash.Class_Initialize", "Failed to obtain access to CryptoAPI, system error " & CStr(Err.LastDllError)
  End If
End Sub
Private Sub HashBlock(ByRef Block() As Byte)
 If CryptHashData(m_hHash, Block(LBound(Block)), UBound(Block) - LBound(Block) + 1, 0&) = 0 Then
   Err.Raise vbObjectError Or &HC312&, "MD5Hash", _
             "Failed to hash data block, system error " _
             & CStr(Err.LastDllError)
  End If
End Sub
Private Function HashValue() As String
Dim lngDataLen As Long, lngHashSize As Long
Dim bytHashValue() As Byte
Dim intByte As Integer
  lngDataLen = 4
  If CryptGetHashParam(m_hHash, HP_HASHSIZE, lngHashSize, lngDataLen, 0&) = 0 Then
    Err.Raise vbObjectError Or &HC322&, "MD5Hash", _
       "Failed to obtain hash value length, system error " & CStr(Err.LastDllError)
  Else
    lngDataLen = lngHashSize
    ReDim bytHashValue(lngDataLen - 1)
    If CryptGetHashParam(m_hHash, HP_HASHVAL, bytHashValue(0), lngDataLen, 0&) = 0 Then
      Err.Raise vbObjectError Or &HC324&, "MD5Hash", _
                "Failed to obtain hash value, system error " & CStr(Err.LastDllError)
    Else
      For intByte = 0 To lngDataLen - 1
        HashValue = HashValue & Right$("0" & Hex$(bytHashValue(intByte)), 2)
      Next
      CryptDestroyHash m_hHash
    End If
  End If
End Function
Private Sub NewHash()
  If CryptCreateHash(m_hProvider, CALG_MD5, 0&, 0&, m_hHash) = 0 Then
         Err.Raise vbObjectError Or &HC332&, "MD5Hash", _
            "Failed to create CryptoAPI Hash object, system error " & CStr(Err.LastDllError)
  End If
End Sub
'----- Public Methods -----
Public Function HashFile(ByVal Filename As String) As String
Const CHUNK As Long = 16384
Dim VeryLargeFile As clsVLBF
Dim cyWholeChunks As Currency
Dim lngRemainder As Long
Dim cyChunk As Currency
Dim bytBlock() As Byte
  On Error Resume Next
  GetAttr Filename
  If Err.Number = 0 Then
    On Error GoTo 0
    Set VeryLargeFile = New clsVLBF
    VeryLargeFile.OpenFile Filename
    cyWholeChunks = Int(VeryLargeFile.FileLen / CHUNK)
    lngRemainder = VeryLargeFile.FileLen - (CHUNK * cyWholeChunks)
    NewHash
    ReDim bytBlock(CHUNK - 1)
    For cyChunk = 1 To cyWholeChunks
      VeryLargeFile.ReadBytes bytBlock
      HashBlock bytBlock
    Next
    If lngRemainder > 0 Then
      ReDim bytBlock(lngRemainder - 1)
      VeryLargeFile.ReadBytes bytBlock
      HashBlock bytBlock
    End If
    VeryLargeFile.CloseFile
    HashFile = HashValue()
  Else
    Err.Raise vbObjectError Or &HC342&, "MD5Hash.HashFile", "File doesn't exist"
  End If
End Function
Public Function HashBytes(ByRef Block() As Byte) As String
  NewHash
  HashBlock Block
  HashBytes = HashValue()
End Function
Private Sub Class_Terminate()
  On Error Resume Next
  CryptDestroyHash m_hHash
  CryptReleaseContext m_hProvider, 0&
End Sub
3. The VeryLargeBinaryFile Class. Put it in a class module named clsVLBF:

Option Explicit
'Add all of this to a class named "clsVLBF"  (Very large binary file)
Public Enum Errors
  UNKNOWN_ERROR = 45600
  FILE_ALREADY_OPEN
  OPEN_FAILURE
  FILELEN_FAILURE
  READ_FAILURE
  FILE_ALREADY_CLOSED
End Enum
Private Const SOURCE = "clsVLBF"
Private Const GENERIC_WRITE As Long = &H40000000
Private Const GENERIC_READ As Long = &H80000000
Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80&
Private Const CREATE_ALWAYS = 2
Private Const OPEN_ALWAYS = 4
Private Const INVALID_HANDLE_VALUE = -1
Private Const INVALID_SET_FILE_POINTER = -1
Private Const INVALID_FILE_SIZE = -1
Private Const FILE_BEGIN = 0, FILE_CURRENT = 1, FILE_END = 2
Private Type MungeCurr
  Value As Currency
End Type
Private Type Munge2Long
  LowVal As Long
  HighVal As Long
End Type
#If VBA7 Then
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" ( _
                        ByVal dwFlags As Long, lpSource As Long, ByVal dwMessageId As Long, _
                        ByVal dwLanguageId As Long, ByVal lpBuffer As String, _
                        ByVal nSize As Long, Arguments As Any) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, _
                        ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, _
                        ByVal lpOverlapped As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, _
                        ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, _
                        ByVal lpOverlapped As Long) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, _
                        ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, _
                        ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, _
                        ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, _
                        lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function FlushFileBuffers Lib "kernel32" (ByVal hFile As Long) As Long
Private hFile As Long
Private C As MungeCurr
Private L As Munge2Long
#Else
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" ( _
                        ByVal dwFlags As Long, lpSource As Long, ByVal dwMessageId As Long, _
                        ByVal dwLanguageId As Long, ByVal lpBuffer As String, _
                        ByVal nSize As Long, Arguments As Any) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, _
                        ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, _
                        ByVal lpOverlapped As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, _
                        ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, _
                        ByVal lpOverlapped As Long) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, _
                        ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, _
                        ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, _
                        ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, _
                        lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function FlushFileBuffers Lib "kernel32" (ByVal hFile As Long) As Long
Private hFile As Long
Private C As MungeCurr
Private L As Munge2Long
#End If
Private sFName As String
Private fEOF As Boolean
Public Property Get FileHandle() As Long
  RaiseErrorIfClosed
  FileHandle = hFile
End Property
Public Property Get FileLen() As Currency
  RaiseErrorIfClosed
  L.LowVal = GetFileSize(hFile, L.HighVal)
  If L.LowVal = INVALID_FILE_SIZE Then
    If Err.LastDllError Then RaiseError FILELEN_FAILURE
  End If
  LSet C = L
  FileLen = C.Value * 10000@
End Property
Public Property Get Filename() As String
  RaiseErrorIfClosed
  Filename = sFName
End Property
Public Property Get EOF() As Boolean
  RaiseErrorIfClosed
  EOF = fEOF
End Property
Public Property Get IsOpen() As Boolean
  IsOpen = hFile <> INVALID_HANDLE_VALUE
End Property
Public Sub CloseFile()
  RaiseErrorIfClosed
  CloseHandle hFile
  sFName = ""
  fEOF = False
  hFile = INVALID_HANDLE_VALUE
End Sub
Public Sub OpenFile(ByVal strFileName As String)
  If hFile <> INVALID_HANDLE_VALUE Then RaiseError FILE_ALREADY_OPEN
  hFile = CreateFile(strFileName, GENERIC_WRITE Or GENERIC_READ, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
  If hFile = INVALID_HANDLE_VALUE Then RaiseError OPEN_FAILURE
  sFName = strFileName
End Sub
Public Function ReadBytes(ByRef Buffer() As Byte) As Long
  RaiseErrorIfClosed
  If ReadFile(hFile, Buffer(LBound(Buffer)), UBound(Buffer) - LBound(Buffer) + 1, ReadBytes, 0) Then
    If ReadBytes = 0 Then fEOF = True
   Else
     RaiseError READ_FAILURE
  End If
End Function

Private Sub Class_Initialize()
  hFile = INVALID_HANDLE_VALUE
End Sub
Private Sub Class_Terminate()
  If hFile <> INVALID_HANDLE_VALUE Then CloseHandle hFile
End Sub
Private Sub RaiseError(ByVal ErrorCode As Errors)
Dim Win32Err As Long, Win32Text As String
  Win32Err = Err.LastDllError
  If Win32Err Then
    Win32Text = vbNewLine & "Error " & Win32Err & vbNewLine & DecodeAPIErrors(Win32Err)
  End If
  If IsOpen Then CloseFile
  Select Case ErrorCode
    Case FILE_ALREADY_OPEN: Err.Raise FILE_ALREADY_OPEN, SOURCE, "File already open."
    Case OPEN_FAILURE: Err.Raise OPEN_FAILURE, SOURCE, "Error opening file." & Win32Text
    Case FILELEN_FAILURE: Err.Raise FILELEN_FAILURE, SOURCE, "GetFileSize Error." & Win32Text
    Case READ_FAILURE: Err.Raise READ_FAILURE, SOURCE, "Read failure." & Win32Text
    Case FILE_ALREADY_CLOSED: Err.Raise FILE_ALREADY_CLOSED, SOURCE, "File must be open for this operation."
    Case Else
      Err.Raise UNKNOWN_ERROR, SOURCE, "Unknown error." & Win32Text
  End Select
End Sub
Private Sub RaiseErrorIfClosed()
  If hFile = INVALID_HANDLE_VALUE Then RaiseError FILE_ALREADY_CLOSED
End Sub
Private Function DecodeAPIErrors(ByVal ErrorCode As Long) As String
Const FORMAT_MESSAGE_FROM_SYSTEM As Long = &H1000&
Dim strMsg As String, lngMsgLen As Long
  strMsg = Space$(256)
  lngMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0&, ErrorCode, 0&, strMsg, 256&, 0&)
  If lngMsgLen > 0 Then
    DecodeAPIErrors = Left(strMsg, lngMsgLen)
  Else
    DecodeAPIErrors = "Unknown Error."
  End If
End Function
Cross posted at: https://www.msofficeforums.com/word-...tml#post183353