Copy Excel Sheet with VBA Loses Protection - vba

Is there a more secure way of protecting excel sheets without embedding the password in the vba code with sheet.Protect code? Required functionality is to copy excel sheets with a macro and retain protection so formulas remain hidden.
Two template sheets have hidden formulas. Copy protection on these sheets allow users to do everything except Edit objects and Edit scenarios.
The sheets need grouping functionality which requires VBA code (added to workbook_Open) to set sheet.Protect UserInterfaceOnly:=True for any sheets with ProtectedContents = True, and .EnableOutlining:=True. All other Protect attributes are set to True in the code except DrawingObjects and Scenarios.
Copying either protected sheet with the excel interface maintains protection on the copy. Copying a sheet in VBA code results in a new sheet with no protection.
My concern is despite protecting the code with a secure password in excel 2010 the code and the embedded password is easily displayed by opening the file in other software. Is there a more secure way of protecting the sheets without embedding the password in the code?
Reference: Retaining Existing Excel Worksheet Protection When Enabling Grouping

By nature, Excel security is not particularly strong. The protection functions exists mainly to prevent lay users from making inadvertent mistakes. However any determined and capable person won't take too long to hack/crack any security you put in place. As an example of how easy it is, take a look at one of the most up-voted questions here:
Is there a way to crack the password on an Excel VBA Project?
Therefore even if you protect the actual VBA project, it's relatively simple to get around this.
In your specific case, you're using a Worksheet_Open event to apply protection - however this is the easiest thing in the world for anyone to bypass, by simply opening the workbook in question via a sub in another workbook, after having turned events off. You may also have some users whose macro security settings are not automatically enabled, and if they dismiss the "Enable Macros?" prompt when they open your workbook, the Open event will never fire.
If you're happy with this level of security - and happy that your workbook won't be the target of hacks from determined users - then you needn't worry too much about storing the protection password in the code itself. However there are a couple of simple ways you can at least make it less visible (in case a user accidentally presses Alt+F11, or an error causes the VBE to open:
Instead of typing the password in the code in plain text, use the Chr() function. So instead of:
Dim pw As String
pw = "hello!"
... you could write:
Dim pw As String
pw = Chr(104) & Chr(101) & Chr(108) & Chr(108) & Chr(111) & Chr(33)
Another way is to store the password within a cell (for example A1) of another sheet whose visibility is set to xlVeryHidden, and reference Sheets("HiddenSheetName").Cells(1,1).Value.
As I said - these are not perfect ways of securing sensitive data and formulæ: however if your goal is simply to stop casual users from making a mistake, or poking around where they shouldn't, then they will do the job.
Additional: for multiple instances requiring the password, I use something like this myself:
Function pw() As String
pw = Chr(104) & Chr(101) & Chr(108) & Chr(108) & Chr(111) & Chr(33)
End Function
Then call it as needed. You can even name the function something else slightly cryptic, not "pw" etc.

Related

Protect an excel sheet allowing the insertion of images via VBA code

I'm trying to protect a WorkSheet allowing the insertion of images. Then I learned how to protect a WorkSheet with VBA code, but the WorkSheet aren't allowed to insert images.
When I manually protect the WorkSheet I discovered that if I check the option "Modify Objects", the WorkSheet allows the insertion of images.
Now, I'm searching about of parameters of the Protect method, then I have the following code:
With Sheets("Sheet1")
.Protect Password:="password", DrawingObjects:=True, Contents:=False, Scenarios:=False
End With
I assumed that the code occupies the parameter DrawingObjects, which should allow the insertion of images, but the code doesn't works properly, the WorkSheet is protected, but still doesn't allow the insertion of images.
I believe your code protects DrawingObjects, rather than allowing them to be inserted. I think it should be DrawingObjects:=False in order to permit this to work.
Whenever I use VBA with a protected worksheet, I first unprotect it, do whatever I want in VBA, then reapply the protection. Or at at least, remove specific protections and then reapply them.

Secure Excel Workbook (with VBA) from reuse with different data

I have created an Excel Workbook with a lot of VBA code for a customer. The customer will provide me with data. I will import that data into the VBA laden template, Save it as an xlsm, and deliver it to the customer. I get paid by the Workbook so I need to prevent them from trying to copy new data into the existing workbook and reusing it.
How can I somehow prevent the customer from reusing a workbook by just entering in new data on the main Worksheet, then saving as a new Workbook, and getting the use of the VBA code for free. (Alternately they could copy the file in windows then enter new data on the copied version.) I need to detect a significant change in data from the initial imported data.
The data on the main sheet is fairly static (perhaps even totally static on many known columns). I'm thinking about randomly sampling some of the cell data on import (perhaps 10 random cells, or number of rows, etc.), and storing that data somewhere. If, say, 50% of the cells change data, I could just disable (or short-circuit) the public entry points in the code...or something else?
I'd like to allow for some flexibility on the part of the customer, but prevent abuse.
Is there a better way than my general idea, above?
Where could I store that data (it should be part of the sheet, but not changeable by the customer). Perhaps a hidden sheet with password locked cells?
Is there some accepted way of doing this that I'm unaware of?
Perhaps Time-expire the functionality in your code
So thank you for this question. Thank you for setting a bounty. It is a very interesting question given your desire to monetise the VBA code, as a VBA programmer I generally resign myself to not being able to monetise VBA code. It is good that you insist and I will contribute an attempt at an answer.
Firstly, let me join the chorus of answers that say VBA is easily hacked. The password protection can be broken. I would join the chorus of respondents who say you should pick a compiled language as the vessel for your code. Why not try C# or VB.NET housed in a Visual Studio Tools For Office (VSTO) .NET assembly?
VSTO will associate a compiled assembly with a workbook. This mechanism is worth knowing because if you insist on VBA we can use the same mechanism (see later). Each workbook has a CustomDocumentProperties collection where one can set custom properties (it says Document not Spreadsheet because the same can be found in Word so Document is the generalised case).
Excel will look at the workbook's CustomDocumentProperties collection and search for "_AssemblyName" and "_AssemblyLocation"; if _AssemblyName is an asterisk then it knows it needs to load a .NET/VSTO assembly, the _AssemblyLocation provides a lookup to the file to load (you'll have to dig in on this, I forget the details). Reference
Anyway, I'm reminded of VSTO CustomDocumentProperties mechanism because if you insist on using VBA then I suggest storing a value in the CustomDocumentProperties collection that helps you time expire the functionality of your code. Note do not use the BuiltInDocumentProperties("Creation Date") because it is easily identifiable; instead use a codeword, say "BlackHawk". Here is some sample code.
Sub WriteProperty()
ThisWorkbook.BuiltinDocumentProperties("Creation Date") = CDate("13/10/2016 19:15:22")
If IsEmpty(CustomDocumentPropertiesItemOERN(ThisWorkbook, "BlackHawk")) Then
Call ThisWorkbook.CustomDocumentProperties.Add("BlackHawk", False, MsoDocProperties.msoPropertyTypeDate, Now())
End If
End Sub
Function CustomDocumentPropertiesItemOERN(ByVal wb As Excel.Workbook, ByVal vKey As Variant)
On Error Resume Next
CustomDocumentPropertiesItemOERN = wb.CustomDocumentProperties.Item(vKey)
End Function
Sub ReadProperty()
Debug.Print "ThisWorkbook.BuiltinDocumentProperties(""Creation Date""):=" & ThisWorkbook.BuiltinDocumentProperties("Creation Date")
Debug.Print "CustomDocumentPropertiesItemOERN(ThisWorkbook, ""BlackHawk""):=" & CustomDocumentPropertiesItemOERN(ThisWorkbook, "BlackHawk")
End Sub
So you could set CustomDocumentProperty "BlackHawk" to the workbook's initial creation time and then allow the client to use the code for 24 hours, or even 48 hours (careful with weekends, create Friday work through to Tuesday) and then afterwards the code can refuse to operate and instead throw a message saying pay LimaNightHawk more money!
P.S. Good luck with your revenue model.
P.P.S. I read your profile, thanks for your military service.
Whatever you do it will be feasible to crack it (VBA code is easy to crack). However:
there is the contract so... that's not legal for them to do it
you can put part of the code on a FTP server and control physically what is being executed
Very nices ideas here though
Compile Excel file to EXE. Google for that.
Concern 1 seems to be basic re-use of the file. You could create a sub in the ThisWorkbook module to destroy code located in other modules in the event that save-as is selected.
Concern 2 seems to be someone hacking your password protection. A similar tactic could be employed such as using "opening the developer window" as your event instead of save as.
I have experimented with save events to log user entries with great success using the ThisWorkbook module. I am not certain how/if one could detect if the developer tab is opened.
Here's what I've done. Perhaps there is a entirely better approach, or there are tweeks to the below that will make it better:
From the VBA Tools menu > VBAProject Properties > Protection (tab), I Locked the project for viewing with a password.
I created a new "License" sheet.
This sheet is hidden from the user. If you hide the sheet via code like this:
Me.Visible = xlSheetVeryHidden
then then sheet cannot be un-hidden by the user (it requires running vba code to unhide).
On initial import I sample:
Number of imported rows
x number of randomly selected cells *from the columns I know won't/shouldn't change. (There are columns they are allowed to change freely.)
I store these on the "License" sheet in Address / Value pairs.
When the workbook is opened, the Workbook_Open event fires and then does a quick comparison of the current number of rows, and the current values for the addresses stored on the "License" sheet. I then do a percentage calculation: If the rows are more than x% different, or the number of changed values is more that y% then I
Sheets(1).Protect LOCKOUT_PASSWORD
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
There is also a method for unlocking the sheets if necessary.
This might be too simple of an answer, but sometimes we fail to think of the simplest solutions:
Why don't you simply provide the customer with only a copy of the output? You could run their data through your macro-enabled workbook, copy the output sheet into a new workbook, and then break all links so that there's no formulas, updating, or ties to your workbook.
I would create another workbook that contains the code and then reference the customers wb from that one.

Can't activate specific worksheet but I can activate others :S

Having some struggles here.. Pretty new to coding and VBA, wrote a code using 2013 without realising I'd have issues moving backward to run on 2010 versions... Derp..
I'm having a bizarre issue.
I'm using worksheets("...").Activate to move between sheets (This was done to reduce the amount of "worksheets("...")." before every line of my code).
Anyway, the macro has no issue activating all the sheets except one. The sheet in question is where the original button is to run the code. I also can't seem to use the activex commandbutton (which is probably the source of my issue).
Note: I have already tried the "delete x files" which was caused by a windows update - this isn't the source of the issue, I can still add new activex controls etc
Edit: I've resolved the issue by changing from the ActiveX control to a button that calls a macro which calls the userform. It seems that something to do with having the ActiveX control on that sheet prevented it from being able to activate through a module. Anyone have an explanation for this?
I don't have an answer as to why you couldn't activate the sheet, and don't think I could answer it without seeing the workbook in question. But, I can help you to avoid using activate in the first place ;)
From your description it sounds like you're using Worksheet(index).Activate so then you can use ActiveSheet.SomeMember to work with the worksheet object.
If this is so, you could save yourself time and make your program more efficient by using a with block.
E.g.:
With Worksheets("...")
Debug.Print "working with """ & .Name & """."
Debug.Print "which belongs to """ & .Parent.Name & """."
End With
And if you need to reference it in multiple places, I'd recommend assigning the object to a variable.
E.g.:
Dim MySheet as Worksheet
Set MySheet = Worksheets("...")
With MySheet
'Do something
End With
MySheet.range("A:A").Copy Destination:= WorkSheets("Someothersheet").Range("B:B")

Where Exactly To Store VBA

We receive Excel files daily from our field offices which I have to clean and re-format (about 110 columns and 500 rows-worth) using VBA.
I need to save my VBA as a macro so we can use it to clean up all the workbook we receive by running the macro and saving the edited sheet as a new worksheet by getting the name from UserForm Combobox items.
Where exactly should I store the VBA snippets? I mean when I open the Visual Basic panel, I have these three options:
Running The Code From Microsoft Excel Object :Sheets1(Sheet1)
Running the Code From An Inserted Module
Running the Code From User Form
If I am supposed to use options 1 or 2, how can I call the UserForm for saving the sheet?
I Recomend you to use modules (Option B)
Option C goes with option B, ill explain, you can create a sub in a module in option B, then you can do:
UserForm1.show
In Option B I would writte this code, but before trying this i recomend you to understand a bit more of vba
sub ClearWBs()
'opening workbook
Workbooks.Open Filename:="c:\book1.xls"
'your code
'your code
'below code for saving and closing the workbook
Workbooks("book1.xls").Activate
ActiveWorkbook.Save
ActiveWorkbook.Close
end sub
Use Module:
If your VBA code focusses on data summarization and manipulation I suggest you use a Module.(example is what you have described in your question).
Use Form:
If what you wan't to do requires a GUI(Graphical User Interface) then you'll have to resort to Form where you can design your GUI. (example is if you have many fields that the user needs to fill-up with distinct values in which you provide the choices)
Use Excel Object:
If what you wan't to do has something to do with events happening on Worksheet and/or Workbook, like for example whenever you add sheet the macro runs, or when you open or close the workbook the macro runs then you will have to write the macro in the Excel Object.
This is basically what i have in mind, hope this helps.
If you receive files that do not contain VBA and you need to apply the same code on those files all the time then I propose that you either save that code in your personal workbook.
You can see how to do that here: http://office.microsoft.com/en-ca/excel-help/copy-your-macros-to-a-personal-macro-workbook-HA102174076.aspx
This is nice because you can also tie it to keyboard shortcut or just have it always ready for you to use.
The disadvantage is that it will only be set up per user session per computer. What you can do is have that code all set up in a module and then import it into your personal workbook if you change session or if someone else has to do it.
Once it's done, you will not have to include the module in your files your receive again.

Excel VBA - Code to move or copy worksheets without warnings

I am trying to create a macro that would act the same as right clicking a workbook tab, selecting move or copy, checking the copy option, selecting another open workbook and clicking ok but without the warnings. I found the code to disable warning and I was able to record a macro that does what I want but I don't know how to make it request which open workbook to copy to.
In short how do I make the following code work where WorksheetIWantToCopy is the one the user currently has selected and OpenWorkbookIWantToCopyToo.xlsx is a workbook to be selected by the user out of a list of open workbooks.
Application.DisplayAlerts = False
Sheets("**WorksheetIWantToCopy**").Select
Sheets("**WorksheetIWantToCopy**").Copy Before:=Workbooks( _
"**OpenWorkbookIWantToCopyToo.xlsx**").Sheets(1)
I appreciate any information anyone can provide. My team greatly appreciates your support (we currently have to hit ok on 25 warnings due to conflicts we don't really care about). Thx!
If the worksheet you want to copy will always be the active sheet then you can use ActiveSheet.
As for letting the user select a workbook, it can be as simple as using the InputBox.
Public Function getWorkbookName() As String
Dim i As Integer, sListOfWbks As String, sRsp As String
' build list of workbooks
For i = 1 To Workbooks.Count
sListOfWbks = sWbkList & vbCrLf & i & " - " & Workbooks(i).Name
Next i
sRsp = InputBox("Select workbook." & vbCrLf & sListOfWbks)
If IsNumeric(sRsp) Then
getWorkbookName = Workbooks(CInt(sRsp)).Name
Else
' user pressed cancel or entered invalid text
getWorkbookName = ""
End If
End Function
This basic example will of course list all workbooks, including hidden add-ins and the workbook you are moving away from.
This needs to be said before anything else: always, always, ALWAYS make use of .Copy instead of .Move when automatically shuffling excel workbooks with VBA. Move has inherent risks because it is a modification of the other file, and if your code misbehaves then you could lose all of the data you're working with.
First of all, know which workbook is which, with no ambiguity:
Dim wkbkDestination, wkbkTemporary As Workbook
Set wkbkDestination = Workbooks("OpenWorkbookIWantToCopyTo.xlsx")
Set wkbkTemporary = Workbooks.Open("WorkbookIWantToCopy.xlsx")
Next, Copy your desired tab to your destination workbook, rename the new tab to prevent errors, and close the second workbook, without saving.
wkbkTemporary.Worksheets("WorksheetIWantToCopy").Copy Before:=wkbkDestination.Worksheets(1)
wkbkDestination.Worksheets(1).Name = "WorkbookIWantToCopy"
wkbkTemporary.Close SaveChanges = False
Naturally, depending on the exact controls you intend to use, there are lots of ways this code could be implemented. From your description it is somewhat unclear what exact problem you're trying to solve and whether this is a one-off event you're trying to accomplish for a given list of files, or whether this function is to be used on an ongoing basis.