Prevent the macro break for pass-protected self-destructing file - excel

I've decided to create a pass-protected file which will self-destruct after three wrong password entries. The macro runs with the file open (UserForm with pass entry field pops up) but the weak point is that Ctrl-Break allows to stop the macro and get access to the code.
Is there any way to disable/prevent Break option in a particular workbook (via VBA, preferably)?
If you are interested, I can provide the macro upon request.
UPD: Here's the macro i'm using (Date based).
Private Sub Workbook_Open()
If Date > Cdate("30/03/2015") Then
WARNING.Show
End If
End Sub
This part of code is assigned to "Ok" and "Cancel" buttons of the UserForm "WARNING".
Public i As Integer
Public b As Integer
Sub setib()
i = 2 - b
b = b + 0
End Sub
Private Sub CnclBtn_Click()
WARNING.Hide
With ThisWorkbook
.Saved = True
.Close False
End With
End Sub
Private Sub OKBtn_Click()
Call setib
Dim Pass As String: Pass = "*your pass*"
Dim PassInput As String: PassInput = WARNING.PassField.Text
If PassInput = Pass Then
MsgBox "Password is correct"
GoTo Safe:
Else
If b < 2 Then
MsgBox "Password is incorrect. " & i & " attempts left."
Else
MsgBox "No More attempts"
End If
If b = 2 Then
WARNING.Hide
GoTo Destroy:
Else
WARNING.PassField.Value = vbNullString
WARNING.PassField.SetFocus
b = b + 1
GoTo EndSub:
End If
End If
Safe:
ActiveWorkbook.VBProject.VBComponents("ThisWorkbook").CodeModule.DeleteLines 1, _
ActiveWorkbook.VBProject.VBComponents("ThisWorkbook").CodeModule.CountOfLines
WARNING.Hide
GoTo EndSub:
Destroy:
With ThisWorkbook
.Saved = True
.ChangeFileAccess xlReadOnly
Kill .FullName
.Close False
End With
EndSub:
Sheet1.Activate
End Sub

You could simply password-protect the VBA project. That will avoid the need to worry about playing with the Application-level settings for EnableCancelKey.
While the user may be able to "break" with the cancel key, they will not be able to view the code without supplying the proper password for the VBA project.
With the VBAProject protected, the user can "break" execution of the code, the user should not be able to enter "break mode", and the Excel application will not be interactive (so the user will not be able to access the worksheets). At this point, the User Form will be frozen on the screen and the application unresponsive. The user then has two options that I can see:
If they know the name of the userform, they could conceivably Unload UserForm1 from the Immediate window in the VBE. So, you should pick some name for the UserForm which they will not likely guess. If they do this, they will be able to access the file itself, but if you give the UF a secure name, they'll never guess it correctly.
Otherwise, they're SOL, unless they Ctrl+AltDel, and kill the Excel application procdess.
NOTE: Excel passwords, whether on the worksheets or the VBAProject (or both) can be cracked by anyone intent on doing so, and are only a good measure to prevent inadvertent corruption or manipulation of the data.

Related

Sheet.activate not activating specified sheet

So I have officially run into a brick wall. I have written a macro that will allow the user, upon password entry, to unlock and unhide all sheets except for the one containing the password. At the end of the macro, I want to go to a specific sheet.
I have tried every reference to that sheet known to mankind, including both sheet code names and indexes, but the "sheet.activate" event just will not trigger. I have tried setting screen updating to true, both before and after the sheet.activate command. Events are enabled. I have disabled all Excel addins. I have tried everything I can possibly think of and everything I've found in forums all over the web. As you'll see from the code, I've even added basic time delays to the code at each step of the activating sequence, all to no avail. I've tried the activate sequence at different parts of the code. At the completion of the code, the best I get is activate the first sheet in my workbook. I even have Option Explicit enabled, and it sheds no light.
Now, the weird thing is, if I take everything out of the code except for the activate events, it works perfectly. If I step through the code line by line, the activate lines work. But if I compile the code and run the whole sub - nada. What the heck am I missing here?
Here is my complete code for this sub:
Option Explicit
Sub UnProtectAll()
Application.ScreenUpdating = False
Dim pPrompt As String
Dim bkPswrd As String
inputPass_box.Show
pPrompt = inputPass_box.passInput.Value
bkPswrd = Worksheets("Password List").Cells(3, 2)
If pPrompt = "" Then
MsgBox "You didn't enter anything...", vbInformation, "No password"
UnProtectAll
ElseIf pPrompt = bkPswrd Then
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
ws.Unprotect bkPswrd
Next ws
ThisWorkbook.Worksheets("Folder History").Visible = xlSheetVisible
ThisWorkbook.Worksheets("Master List").Visible = xlSheetVisible
ThisWorkbook.Worksheets("File Formats").Visible = xlSheetVisible
Unload inputPass_box
Worksheets("Change Sheet").Shapes("Button 1").Visible = False
Application.ScreenUpdating = True
Application.Wait (Now + TimeValue("0:00:03"))
ThisWorkbook.Worksheets("Folder History").Select
ThisWorkbook.Worksheets("Folder History").Activate
Application.Wait (Now + TimeValue("0:00:03"))
ThisWorkbook.Worksheets("Change Sheet").Select
ThisWorkbook.Worksheets("Change Sheet").Activate
Else
MsgBox "You have entered an incorrect password. Please check your password and try again.", vbCritical, "Wrong Password!"
UnProtectAll
End If
End Sub
Nevermind. I just figured it out, but here's my answer in case someone else needs help with the same issue. The line
Unload inputPass_box
was throwing it off for some reason ("inputPass_box" is a UserForm used as an input box). If someone would be so kind as to set me straight on this, I would be much obliged, but to my very limited knowledge, it seems that this line triggers the UserForm_QueryClose event, as in my project this event contains and "End" command to prevent the userform from passing any data back to the sub, as this would trigger several other events when I want closing or cancelling the user form to stop rather than displaying any of the error messages contained in the code block above.
pPrompt = InputBox("please input password")
bkPswrd = Worksheets("Password List").Cells(3, 2)
If pPrompt = "" Then
MsgBox "You didn't enter anything...", vbInformation, "No password"
UnProtectAll
ElseIf pPrompt = bkPswrd Then
use "InputBox" no problem,There may be a problem with the inputPass box

How to keep a userform open when closing other instances of Excel

Hope someone can help - I have a Userform that opens on launch of the excel file (Test.xlsm) and hides the workbook from prying eyes. The workbook can become visible for editing by a button click and password entry from the userform. Everything is working fine - UNTIL - you open another instance of excel. Once you finish with it and close any secondary instance of excel, it also either
1. closes the userform, or
2. shows the excel workbook behind the userform. Neither of these is what I want. I need the userform to remain open and I need the workbook associated with it to remain hidden until called.
Question - is there some code that will prevent other instances of excel from doing what it is doing, or am I dreaming.
I found some code (below) that the writer said done exactly what i am after, but all I got was global errors.
Private Sub WorkBook_Open()
If Workbooks.Count = 1 Then Application.Visible = False
Workbooks("test.xlsm").Windows(1).Visible = False
UserForm1.Show vbModeless
End Sub
Any help greatly appreciated.
BTW - the code for the workbook open is
Private Sub Workbook_Open()
Set Thiswb = Me.Application
Application.Visible = False
Staff_Contacts.Show vbModeless
End Sub
For usecase you described, I would propose you something similar to this solution:
Sub Workbook_open()
Dim wrong_attempts As Integer
wrong_attempts = 0
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Sheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
ActiveWorkbook.Sheets("Sheet2").Visible = xlSheetVeryHidden
Start1:
If wrong_attempts > 4 Then
MsgBox "You've entered wrong password too many times. File will now be closed"
Application.DisplayAlerts = False
sheet.Delete
ActiveWorkbook.Close
Application.DisplayAlerts = True
End If
InputBoxVariable = InputBox(Prompt:="Please enter password to access this document", Title:="Authorization required", Default:="")
If InputBoxVariable = "12345" Then
ActiveWorkbook.Sheets("Sheet2").Visible = xlSheetVisible
Application.DisplayAlerts = False
sheet.Delete
Application.DisplayAlerts = True
Else
wrong_attempts = wrong_attempts + 1
GoTo Start1
End If
End Sub
In this example it is assumed that sheet2 should be protected. On the very load of the document, phantom sheet will be created, and after successful login, it will be deleted and sheet2 should be visible again. In addition, if someone enters wrong password 5 times, file closes automatically.
Fair notice, I've used InputBox out here, so password is not masked, if you want to mask password as well, you will have to make brand new form with button and textbox.

Restrict viewing access to an Excel worksheet

I thought this would be a readily used function in Excel but it's surprisingly difficult to implement a simple process of restricting access to specific worksheets within a larger workbook.
There's a few methods that prompt an initial password to open various versions of the same workbook. But I want to keep the workbook identical for all users but restrict access to certain sheets. Surely there's a password protect function that requires the user to enter a password to view a sheet. Rather than create multiple versions of the same workbook based on different users.
I have tried the following but it doesnt prompt a password to access the sheet
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
Dim MySheets As String, Response As String
Dim MySheet As Worksheet
MySheet = "COMMUNICATION"
If ActiveSheet.Name = MySheet Then
ActiveSheet.Visible = False
Response = InputBox("Enter password to view sheet")
If Response = "MyPass" Then
Sheets(MySheet).Visible = True
Application.EnableEvents = False
Sheets(MySheet).Select
Application.EnableEvents = True
End If
End If
Sheets(MySheet).Visible = True
End Sub
Am I doing this right?
It sounds like according to the comments that this isn't as much as a security issue as it is a convenience issue. So please bear in mind when considering implementing this into your project that this is easily breakable if there is any malicious intent to gain unauthorized access.
First, I would recommend a common landing zone. A main worksheet that is displayed immediately after opening a workbook. To do this, we would use the Workbook_Open() event and activate a sheet from there.
This can be a hidden sheet if desired, that will be up to you.
Option Explicit
Private lastUsedSheet As Worksheet
Private Sub Workbook_Open()
Set lastUsedSheet = Me.Worksheets("MainSheet")
Application.EnableEvents = False
lastUsedSheet.Activate
Application.EnableEvents = True
End Sub
Next, we should decide on what should occur when there's an attempt to access a new sheet. In the below method, once a sheet is activated it will automatically redirect the user back to the last used sheet until a successful password attempt has been made.
We can track the last used sheet in a module-scoped variable, which in this example will be named lastUsedSheet. Any time a worksheet is successfully changed, this variable will be set to that worksheet automatically - this way when when someone attempts to access another sheet, it will redirect them back to the prior sheet until the password is successfully entered.
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
On Error GoTo SafeExit
Application.EnableEvents = False
' Error protection in case lastUsedSheet is nothing
If lastUsedSheet Is Nothing Then
Set lastUsedSheet = Me.Worksheets("MainSheet")
End If
' Allow common sheets to be activated without PW
If Sh.Name = "MainSheet" Then
Set lastUsedSheet = Sh
Sh.Activate
GoTo SafeExit
Else
' Temporarily send the user back to last sheet until
' Password has been successfully entered
lastUsedSheet.Activate
End If
' Set each sheet's password
Dim sInputPW As String, sSheetPW As String
Select Case Sh.Name
Case "Sheet1"
sSheetPW = "123456"
Case "Sheet2"
sSheetPW = "987654"
End Select
' Create a loop that will keep prompting password
' until successful pw or empty string entered
Do
sInputPW = InputBox("Please enter password for the " & _
"worksheet: " & Sh.Name & ".")
If sInputPW = "" Then GoTo SafeExit
Loop While sInputPW <> sSheetPW
Set lastUsedSheet = Sh
Sh.Activate
SafeExit:
Application.EnableEvents = True
If Err.Number <> 0 Then
Debug.Print Time; Err.Description
MsgBox Err.Description, Title:="Error # " & Err.Number
End If
End Sub
Side note, disabling events is necessary due to the fact that your Workbook_SheetActivate event will continue to fire after a successful sheet change.
Preventing file type changes during SaveAs1
You can further protect the accidental removal of VBA code by restricting the file save type. This can be accomplished using the Workbook_BeforeSave() event. The reason this is a potential problem is that saving as a non-macro enabled workbook will erase the code, which will prevent the password protection features you just implemented above.
First, we need to check if this is a Save or SaveAs. You can accomplish this using the Boolean property SaveAsUI that is included with the event itself. If this value is True, then it's a SaveAs event - which means we need to perform additional checks to ensure that the file type isn't accidentally changed from the save dialog box. If the value is False, then this is a normal save, and we can bypass these checks because we know the workbook will be saved as type .xlsm.
After this initial check, we will display the dialog box using Application.FileDialog().Show.
Afterwards, we will check if the user canceled the operation .SelectedItems.Count = 0 or clicked Save. IF user clicked cancel, then we simply set Cancel = True and the workbook will not save.
We proceed to check the type of extension selected by the user using this line:
If Split(fileName, ".")(UBound(Split(fileName, "."))) <> "xlsm" Then
This will split the file path by a period ., and will grab the last instance of the period (UBound(Split(fileName, "."))) in the event a file name may contain additional periods. If the extension does not match xlsm, then we abort the save operation.
Finally, after all checks passed, you can save the document:
Me.SaveAs .SelectedItems(1), 52
Since we already saved it with the above line, we can go ahead and set Cancel = True and exit the routine.
The full code (to be placed in the Worksheet obj module):
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
On Error GoTo SafeExit
If SaveAsUI Then
With Application.FileDialog(msoFileDialogSaveAs)
.Show
If .SelectedItems.Count = 0 Then
Cancel = True
Else
Dim fileName$
fileName = .SelectedItems(1)
If Split(fileName, ".")(UBound(Split(fileName, "."))) <> "xlsm" Then
MsgBox "You must save this as an .xlsm document. Document has " & _
"NOT been saved", vbCritical
Cancel = True
Else
Application.EnableEvents = False
Application.DisplayAlerts = False
Me.SaveAs .SelectedItems(1), 52
Cancel = True
End If
End If
End With
Else
Exit Sub
End If
SafeExit:
Application.EnableEvents = True
Application.DisplayAlerts = True
If Err.Number <> 0 Then
Debug.Print Time; Err.Description
MsgBox Err.Description, Title:="Error # " & Err.Number
End If
End Sub
1 Shoutout to PatricK for the suggestion
If you want to restrict access to a worksheet, you can just hide it:
ActiveWorkbook.Sheets("YourWorkSheet").Visible = xlSheetVeryHidden
I concur with Mathieu Guindon that any VBA attempt to "Restrict viewing access to an Excel worksheet" will be flimsy as explained by Mathieu Guindon. Moreover, If the file is opened with Excel option Macro security level other than the lowest, any VBA code including this is bound to fail.
However just for shake of simplicity I prefer to use workbook open event and Sheet Activate of the restricted sheet. Using Workbook Sheet Activate event will trigger password prompt even during switching between sheets by user with viewing access.
Private Sub Workbook_Open()
Sheets("COMMUNICATION").Visible = xlSheetHidden
End Sub
Public ViewAccess As Boolean 'In restricted sheet's activate event
Private Sub Worksheet_Activate()
If ViewAccess = False Then
Me.Visible = xlSheetHidden
response = Application.InputBox("Password", xTitleId, "", Type:=2)
If response = "123" Then
Me.Visible = xlSheetVisible
ViewAccess = True
End If
End If
End Sub

MsgBox within an If statement not working

I'm trying to use the following code to open a password protected file if the Windows user is "bhope" or "jdean" and display a message box if the user is anyone else. It opens the file as needed when the user is "bhope" or "jdean" but if another user clicks the button, nothing happens/no error. What am I missing?
Sub Button1_Click()
Dim wb As Workbook
Dim strUser As String
strUser = Environ("USERNAME")
Application.ScreenUpdating = False
Select Case strUser
' Full Workbook Access
Case Is = "bhope", "jdean"
If ActiveWorkbook.ReadOnly Then _
Set wb = Workbooks.Open(Filename:="M:\...", Password:="TEST")
' Limit Access
Case Is = "mjackson" 'also tried "Case Is <> "bhope", "jdean"
If Not ActiveWorkbook.ReadOnly Then _
MsgBox ("This button is reserved for SAMs")
End Select
Application.ScreenUpdating = True
End Sub
If it helps, I used this link to start the base of the code and tried to modify it from there. Thanks and cheers!
Your use of IS here may be the culprit. At best it's superfluous, at worse it's masking this issue. Instead try:
Sub Button1_Click()
Dim wb As Workbook
Dim strUser As String
strUser = Environ("USERNAME")
Application.ScreenUpdating = False
Select Case strUser
' Full Workbook Access
Case "bhope", "jdean"
If ActiveWorkbook.ReadOnly Then _
Set wb = Workbooks.Open(Filename:="M:\...", Password:="TEST")
' Limit Access
Case "mjackson"
If Not ActiveWorkbook.ReadOnly Then _
MsgBox ("This button is reserved for SAMs")
End Select
Application.ScreenUpdating = True
End Sub
Also consider changing that second CASE to CASE ELSE.
The other thing is that your msgbox is inside of your IF condition. strUser must be uqual to mjackson AND the ActiveWorkbook (whatever that might be at the time this code executes) must NOT be ReadOnly for that msgbox to fire.
Consider changing "ActiveWorkbook" to be more specific. Perhaps ThisWorkbook.ReadOnly?
Consider an Else for your if statement to see if the mjackson is hitting but the readonly is not:
Case "mjackson"
If Not ActiveWorkbook.ReadOnly Then
MsgBox ("This button is reserved for SAMs")
Else
MsgBox ("ActiveWorkbook is not Read Only so yo get this message")
End If
Lastly, put a breakpoint (F9) on SELECT and see what your value of strUser as while the code is running (hover over strUSer on that line or check your Locals pane). You may also want to see what ActiveWorkbook is at this point of time too, just in case. The answer will, again, be in your Locals pane, so make sure that is turned on in the view drop down of VBE.
I figured out the solution. Apparently the "case else" I said I tried earlier was actually done to another test file with similar code as I usually have several open when I'm testing to compare behaviors. I also had to delete the line below "case else" so that only the msg box line would run after that. Below is the code I used should anyone need it in the future:
Sub Button1_Click()
Dim wb As Workbook
Dim strUser As String
strUser = Environ("USERNAME")
Application.ScreenUpdating = False
Select Case LCase(strUser)
' Full Workbook Access
Case Is = "bhope", "jdean"
If ThisWorkbook.ReadOnly Then _
Set wb = Workbooks.Open(Filename:="M:...", Password:="TEST")
' Limit Access
Case Else
MsgBox ("This button is reserved for SAMs")
End Select
Application.ScreenUpdating = True
End Sub
To answer earlier questions: my understanding of screen update is that if it's turned off, the application will run non-visually so as not to cause concern for the users who will be using this file. Also, this code makes it run faster, does it not?
As far as the purpose of the workbook and security concerns... the button will be used on a read only workbook that houses many portions of our company's metrics. The file is made read only so that users cannot save over it. Since the file is kind of big and a lot of data is expected to go into it, my thought process was to load the "Shell" of the main file then have buttons that determine who should be allowed to add info to certain sheets. By having both files read only and password-protected, I'm able to open the 2nd sheet when the appropriate user clicks the button, then have data transfer back and forth between the workbooks. I still intend on password protecting the VBA code so I don't see how their could be a security concern. Also, #ashleedog, I meant that everyone in our company has lower case user names.

Login attempts and error handling in VBA Excel

I have been trying to fix a login problem but I cannot find a solution. When both login and pass fail, an error message starts a countdown without letting the user manifest another opinion.
QUESTION 1: Can anyone please make the necessary corrections without altering too much the given code structure and explain?
QUESTION 2: What code would turn the "User1" text into bold at the moment the access is granted?
QUESTION 3: What command would disable the "X" on the top right-hand corner of the msg form?
Thank you in advance
Here it is what I could do
¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
Private Sub BtOK_Click()
Dim User1 As String
Dim count As Integer
count = 3
MM:
If EDBoxlogin.Value = "admin" And EDBoxpass.Value = "1234" Then
User1 = Application.UserName
MsgBox "welcome" & User1 & " !", vbExclamation, "Access Granted"
Sheets("Plan1").Visible = xlSheetVisible
Unload Me
Else
If EDBoxlogin.Value = "" Or EDBoxpass.Value = "" Then
MsgBox "Please, fill in the fiels 'login' and 'pass'", vbExclamation + vbOKOnly, "Access denied : incomplete information"
Else
If count >= 0 Then
MsgBox "Login and pass are incorrect! You have " & count & " more trial(s)", vbExclamation + vbOKOnly, "Access denied"
EDBoxlogin.Value = "" And EDBoxpass.Value = ""
' I want to delete previous text in the editbox fields
count = count - 1
GoTo MM
Else
ThisWorkbook.Close
End If
End If
End If
End Sub
If you don't really need to know which user is opening the workbook, consider using Excel's built-in password security function. Also, you should encrypt the contents of the file also using Excel's built-in functions, or anyone can open the file with a text editor and find the userID and password listed in your code.
If you must use a login form, and I've also had to do so in the past, the following code builds on what you did by adding a user list to a hidden worksheet Users. Column A in that sheet needs to be the user names, B contains the passwords. This worksheet also uses cell D1 to track failed login attempts. Using variables in code for this sort of thing is tough ... you have to make them Public and if there are any errors when running code, it will lose its value, then bad things can happen.
The code also references another sheet, SplashPage. This allows you to hide Project1 when the user exits the workbook. The code I wrote handles the hide/unhide process when the file is opened or closed.
I don't know a way to turn off the close box in a user form. I've added code to reject the login if a user does that.
Happy coding.
'Module: frmLogin
Private Sub BtOK_Click()
Dim User1 As String
Dim Passwd As Variant
Sheets("Users").Range("D2").Value = False
User1 = EDBoxlogin.Value
Passwd = getPassword(User1)
If User1 <> "" And Passwd <> "" And EDBoxpass.Value = Passwd Then
Sheets("Users").Range("D2").Value = True
MsgBox "Welcome " & User1 & "!", vbExclamation, "Access Granted"
With Sheets("Plan1")
.Visible = xlSheetVisible
.Activate
End With
Sheets("SplashPage").Visible = xlSheetVeryHidden
Unload Me
Exit Sub
Else
Sheets("Users").Range("D1").Value = Sheets("Users").Range("D1").Value - 1
If Sheets("Users").Range("D1").Value > 0 Then
MsgBox "Login and pass are incorrect! You have " & Sheets("Users").Range("D1").Value & _
" more trial(s)", vbExclamation + vbOKOnly, "Access denied"
EDBoxpass.Value = ""
With EDBoxlogin
.Value = ""
.SetFocus
End With
' I want to delete previous text in the editbox fields
Exit Sub
End If
End If
UserForm_Terminate
End Sub
Private Sub UserForm_Terminate()
If Sheets("Users").Range("D2").Value <> True Then
MsgBox "Login cancelled, goodbye!"
doWorkbookClose
End If
End Sub
'Module: ThisWorkbook
Private Sub Workbook_BeforeClose(Cancel As Boolean)
doWorkbookClose
End Sub
Private Sub Workbook_Open()
On Error Resume Next
Sheets("Users").Range("D1").Value = 3
With Sheets("SplashPage")
.Visible = xlSheetVisible
.Activate
End With
Sheets("Plan1").Visible = xlSheetVeryHidden
Sheets("Users").Visible = xlSheetVeryHidden
ThisWorkbook.Save
frmLogin.Show
End Sub
'Module: Module1
Function getPassword(strVarib As String) As Variant
Dim r As Long
Dim sht As Worksheet
Dim rng As Range
On Error GoTo ErrorHandler
Set sht = Sheets("Users")
Set rng = sht.Range("A:A")
r = WorksheetFunction.Match(strVarib, rng, 0)
getPassword = sht.Cells(r, 2).Value
Exit Function
ErrorHandler:
getPassword = Empty
End Function
Sub doWorkbookClose()
On Error Resume Next
With Sheets("SplashPage")
.Visible = xlSheetVisible
.Activate
End With
Sheets("Plan1").Visible = xlSheetVeryHidden
Sheets("Users").Visible = xlSheetVeryHidden
ThisWorkbook.Save
End Sub
[begin Q&A]
Luiz, I've answered your edits below.
'Q: What Passwd does?
'Module: frmLogin
....
Passwd = getPassword(User1)
A: It gets the password value matching the value of User1. Here's the whole function for context:
Function getPassword(strVarib As String) As Variant
Dim r As Long
Dim sht As Worksheet
Dim rng As Range
On Error GoTo ErrorHandler
Set sht = Sheets("Users")
Set rng = sht.Range("A:A")
r = WorksheetFunction.Match(strVarib, rng, 0)
getPassword = sht.Cells(r, 2).Value
Exit Function
ErrorHandler:
getPassword = Empty
If User1 does not exist then WorksheetFunction.Match throws an error and code execution will jump to ErrorHandler:.
'Q: Does Empty mean that the cell is not with zeros or spaces, but completely blank instead?
A: Empty refers to a Variant variable type that is set to its default value. getPassword could just as easily return the boolean False or integer 0 because those are the default values for those types. It's actually not strictly necessary to set getPassword to anything here ... it's just my personal practice to be explicit.
Since IsEmpty(celFoo) is a valid test for whether a cell is empty or not, you might want to return False instead of Empty to avoid ambiguity.
'Q: Can you explain these two lines below in detail?
Set sht = Sheets("Users")
Set rng = sht.Range("A:A")
A: It's just habit. The alternative would be to elminate those variable assignments and rewrite this line:
r = WorksheetFunction.Match(strVarib, rng, 0)
as:
r = WorksheetFunction.Match(strVarib, Sheets("Users").Range("A:A"), 0)
which is messier to type. Especially if we're going to be doing other things on that sheet with that range in the same routine. Which we are in the next block of code ...
'Q: Important to explain these three lines below in detail too [why 0?, To where (r,2) points to?]
r = WorksheetFunction.Match(strVarib, rng, 0)
getPassword = sht.Cells(r, 2).Value
Exit Function
A: To review, worksheet Users contains user IDs in column A, and their passwords in column B. There can be as many users as there are rows in a worksheet.
- rng is column A as set above.
- 0 means find an exact match for strVarib and throw an error if not match is found.
- If we find a match, r will be set to the row number where the value in column A is equal to our input parameter, strVarib.
- So, sht.Cells(r, 2).Value is the password value in column B (column 2) for the UserID.
'Q: Why the need to call a splashpage? What it contains?
A: You don't necessarily need one, but if you really want to secure your workbook it's good practice. Let's say that it contains sensitive information that you don't want unauthorized user to see. At the very least you would:
Encrypt the worbook using native Excel functionality.
Password protect your VBA project using native functionality. This keeps savvier users from reading your code and making the xlSheetVeryHidden sheets Users and Plan1 visible to their prying eyes.
Now, you can't hide all sheets in a workbook at the same time, at least one needs to be visible at any given time ...
... so I've created a third sheet called SplashPage that doesn't contain any sensitive information. And that means I can hide all of the other worksheets until the user enters a valid UserID and password in frmLogin.
SplashPage can contain whatever you want. You can call it whatever you want. Typically, mine says something like:
Welcome to the Enemies List Application!
Only authorized users may access this workbook.
If you're seeing this page and no login form is visible
it means you've disabled the macros in this workbook.
Please make sure macro security is set to "Medium"
then close Excel entirely, reopen this file
and select "Enable Macros" when prompted.
If you attempt to view or modify this file without proper
authorization you will be added to the list herein.
-[Signed] Richard M. Nixon
A really really secure workbook would not contain the users and passwords in a hidden sheet. In fact, I never do this. Most of my apps are database driven, and I authenticate users against both the domain and a custom table in the application database. This effectively keeps anyone from using it unless they're onsite and connected to the network. I also usually flush all the data from the relevant worksheets when the user closes the workbook to a) keep the file size smaller and b) keep sensitive data from being stored in it and taken offsite. But that's beyond the original scope of your question.
'Why is [the following] necessary? What is being saved? Purpose?
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ThisWorkbook.Save
A: There are two scenarios for closing the application: 1) a failed login attempt and 2) a successful login by a user who has finished making changes.
Take case (2) first. We want to hide all the sensitive information before closing so that the next person who opens the file only sees SplashPage and the login form. We know the user is closing the workbook because we have this code in the ThisWorkbook module BeforeClose event script:
'Module: ThisWorkbook
Private Sub Workbook_BeforeClose(Cancel As Boolean)
doWorkbookClose
End Sub
All it does is call this subroutine in Module1:
Sub doWorkbookClose()
On Error Resume Next
With Sheets("SplashPage")
.Visible = xlSheetVisible
.Activate
End With
Sheets("Plan1").Visible = xlSheetVeryHidden
Sheets("Users").Visible = xlSheetVeryHidden
ThisWorkbook.Save
End Sub
Since our close routine makes changes to the workbook to hide sensitive information, those changes need to be saved. If ThisWorkbook.Save wasn't there, Excel would prompt the user if they wanted to save "their" changes. Which is annoying at best, confusing at worst, because most users will have already pressed "Save" before closing. And if we give them the option here now to close without saving, then we run the risk of all those sensitive worksheets we've just made xlVeryHidden visible to the next user. And that next user could be a bad guy who knows how to disable macros (or anyuser who simply has macro security set above Medium) which means that the following code wouldn't run:
Private Sub Workbook_Open()
On Error Resume Next
Sheets("Users").Range("D1").Value = 3
With Sheets("SplashPage")
.Visible = xlSheetVisible
.Activate
End With
Sheets("Plan1").Visible = xlSheetVeryHidden
Sheets("Users").Visible = xlSheetVeryHidden
ThisWorkbook.Save
frmLogin.Show
End Sub
which is my semi-paranoid-self trying to make it as sure as possible that the next user opening this file doesn't see something I don't want them to.
Note that none of this secuity is bomb-proof. It will lock out most average Excel users that you don't want in it, but someone who knows more about VBA than I do could probably find a way in.
Yes, that was an invitation. :)

Resources