I am writing a macro where there is a central input sheet – let us call this sheet – "Main Input sheet" where user inputs the variables concerned. In the "Main Input sheet" there are some inputs say – "Any More Input sheets?" – which when "Yes", a worksheet corresponding to the input is displayed (it was previously hidden) – Lets call it "Associated Input sheet". Now, I want to ensure that the user updates "Associated Input sheet" before he runs the macro. Is there a way I can do this – using event handlers that VBA provides or using any other way?
Excel – Check if the worksheet is updated before running the macro in VBA
eventsexcelvba
Related Solutions
You can try this direct VBA
approach which doesn't require HEX editing. It will work for any files (*.xls, *.xlsm, *.xlam ...).
Tested and works on:
Excel 2007
Excel 2010
Excel 2013 - 32 bit version
Excel 2016 - 32 bit version
Looking for 64 bit version? See this answer
How it works
I will try my best to explain how it works - please excuse my English.
- The VBE will call a system function to create the password dialog box.
- If user enters the right password and click OK, this function returns 1. If user enters the wrong password or click Cancel, this function returns 0.
- After the dialog box is closed, the VBE checks the returned value of the system function
- if this value is 1, the VBE will "think" that the password is right, hence the locked VBA project will be opened.
- The code below swaps the memory of the original function used to display the password dialog with a user defined function that will always return 1 when being called.
Using the code
Please backup your files first!
- Open the file(s) that contain your locked VBA Projects
Create a new xlsm file and store this code in Module1
code credited to Siwtom (nick name), a Vietnamese developer
Option Explicit Private Const PAGE_EXECUTE_READWRITE = &H40 Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As Long, Source As Long, ByVal Length As Long) Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As Long, _ ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long Private Declare Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As Long Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, _ ByVal lpProcName As String) As Long Private Declare Function DialogBoxParam Lib "user32" Alias "DialogBoxParamA" (ByVal hInstance As Long, _ ByVal pTemplateName As Long, ByVal hWndParent As Long, _ ByVal lpDialogFunc As Long, ByVal dwInitParam As Long) As Integer Dim HookBytes(0 To 5) As Byte Dim OriginBytes(0 To 5) As Byte Dim pFunc As Long Dim Flag As Boolean Private Function GetPtr(ByVal Value As Long) As Long GetPtr = Value End Function Public Sub RecoverBytes() If Flag Then MoveMemory ByVal pFunc, ByVal VarPtr(OriginBytes(0)), 6 End Sub Public Function Hook() As Boolean Dim TmpBytes(0 To 5) As Byte Dim p As Long Dim OriginProtect As Long Hook = False pFunc = GetProcAddress(GetModuleHandleA("user32.dll"), "DialogBoxParamA") If VirtualProtect(ByVal pFunc, 6, PAGE_EXECUTE_READWRITE, OriginProtect) <> 0 Then MoveMemory ByVal VarPtr(TmpBytes(0)), ByVal pFunc, 6 If TmpBytes(0) <> &H68 Then MoveMemory ByVal VarPtr(OriginBytes(0)), ByVal pFunc, 6 p = GetPtr(AddressOf MyDialogBoxParam) HookBytes(0) = &H68 MoveMemory ByVal VarPtr(HookBytes(1)), ByVal VarPtr(p), 4 HookBytes(5) = &HC3 MoveMemory ByVal pFunc, ByVal VarPtr(HookBytes(0)), 6 Flag = True Hook = True End If End If End Function Private Function MyDialogBoxParam(ByVal hInstance As Long, _ ByVal pTemplateName As Long, ByVal hWndParent As Long, _ ByVal lpDialogFunc As Long, ByVal dwInitParam As Long) As Integer If pTemplateName = 4070 Then MyDialogBoxParam = 1 Else RecoverBytes MyDialogBoxParam = DialogBoxParam(hInstance, pTemplateName, _ hWndParent, lpDialogFunc, dwInitParam) Hook End If End Function
Paste this code under the above code in Module1 and run it
Sub unprotected() If Hook Then MsgBox "VBA Project is unprotected!", vbInformation, "*****" End If End Sub
Come back to your VBA Projects and enjoy.
Yes, they are named LONGFI~1.DLL and LONGFI~2.DLL (all letters capitalized in 8.3 file format). Which is which is determined by the order they are created.
In a cmd window you can type dir /x to view the short file names as well.
Best Answer
The Worksheet_Change event procedure is probably the way to go, unless you've got other stuff happening elsewhere on the sheet that makes lots of changes.
At that point, the your question can be rephrased: 'Has my range changed since I checked last?'
Grabbing a copy of the range and storing it somewhere, and checking the current range against the cached copy, cell-by-cell, is a brute force approach: it's OK if you're only doing it once, but if you're doing it repeatedly it's more efficient to store a hash - a short code or number generated by some kind of checksum function.
Checksum algorithms vary. Adler32 is simple and quick, but it performs badly - you get 'Hash Collisions' or failures to return differing hashes for data inputs that differ - on comparisons of (say) a pair of single words of 6-10 letters. However, it performs very well indeed when asked to detect changes to a column of 24 8-letter words, or to a table of a few thousand dates and numbers.
Look up other hashes - and keep up-to-date: your PC will have several libraries with hashes like MD5 and sha1, which should perform better than a hand-rolled hash in VBA.
Here's some demonstration code using the Adler-32 checksum. Read the code comments, there's stuff in there you'll need to know in order to adapt this to your project: