Copy Method of Worksheet class fails with .xlam file - vba

CONTEXT: I have created an Excel Add-In in which there is a worksheet named "Mother". When the user clicks a button from any workbook in which the Add-In is installed, I would like the worksheet "Mother" to be copied inside its same workbook and being renamed. In code:
ThisWorkbook.Sheets("Mother").Copy After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
WHAT I EXPECT: being the line of code above placed into the .xlam code, I know that ThisWorkbook refers to the Add-In and this is observable through a watcher as well:
So, I expect that a copy of "Mother" will be added to the workbook, at the end.
WHAT HAPPENS: the method simply fails, returning the error Copy Method of Worksheet class failed.
WHAT I HAVE TRIED: By simply looking for the name of the error online I have figured out that, apparently, I cannot copy a hidden worksheet and that's why the method fails. So I have tried:
1) To change the visibility with ThisWorkbook.Sheets("Mother").Visible = True... No success;
2) To activate the workbook before/after to change the visibility, so ThisWorkbook.Activate... No success.
I'm not being able to find much documentation online about this, so I'm almost wondering if it's actually possible to edit/show the workbook.xlam, something that I would really need to do for my project.
A BIT MORE OF CONTEXT: The reason why I need to both add the worksheet and show it to the user is that the Add-In, which basically consists in a custom function =myFunction(), needs to be fed by user-inputs for make its calculus.
In particular, neither I can write the data in the sheet before to distribute the Add-In (because I don't know the user inputs in advance), nor I can prepare a user-form where to insert the data and show it at running-time (because the user-inputs are usually a big amount of data that can be copied-pasted easily into an Excel spreadsheet, but not inserted manually into a form nor in the function parameters themselves). So:
MY QUESTION: Is it possible to copy (duplicate) a worksheet within a .xlma workbook and activate it after to allow the user data insertion? If yes, where am I being wrong on the above?

Looks like this link would help: http://forums.techguy.org/business-applications/1120165-add-extra-sheet-xlam-file.html.
The operation cannot be performed on add-in workbook, hence you just need to set it as "not add in":
ThisWorkbook.IsAddIn = False
'your copy operation
This Workbook.IsAddIn = True

Related

VBA - excel closes the previous workbook on opening the new one

I have a strange problem, I suscpect it's connected to the version of the Excel, but I'm not sure at all. I can't figure it out alone so I need your help. I have a macro, which operates on a fresh workbook - it's not saved anywhere, as the worker will save it manually afterwards. The macro is a .xlam format add-in, adding a couple of buttons to the ribbon and these buttons start the code.
Inside the code I have simple lines for opening a new workbook, chosen earlier by an user:
Application.DisplayAlerts = False
Set wbMPA = Workbooks.Open(MPA_file)
Application.DisplayAlerts = True
Earlier, the code sets active workbook as an object/workbook the macro will mainly work on (tried both versions):
Set dwb = Application.ActiveWorkbook
and later in the code
dwb.activate
OR:
dwb = ActiveWorkbook.Name
and then
workbooks(dwb).Activate
The lines are in separate subs, but the variable is globally declared.
The code works fine until the opening of wbMPA (watching it in the locals all the time). When I try to open the new file with the code above, the earlier workbook (dwb) just closes itself from unknown reasons.
The error I get from the 1st method is this:
error screenshot
The second one gives a simple "Subscipt out of range".
The errors, however, are not a problem. The problem is the cause of them, which is closing of the workbook from unknown reasons.
It happens only when I open the completely new workbook (using the excel icon on the Start bar) - when I do it from File -> New -> Blank Workbook using already opened workbook, the error does not occur.
Another strange thing - me and my colleague from work use 2013 version of Excel. I never have this error, she has it every time.
This is a general scheme of the code, other things are meaningless in this case because there are no other manipulations of workbooks/worksheets.
Dim dwb As Object
Dim wbMPA As Object
Sub_1()
Set dwb = ActiveWorkbook
Set wbMPA = Workbooks.Open(MPA_file)
Call Sub_2
End Sub
Sub_2()
dwb.Activate
End Sub
I get an error on the activation of dwb in Sub_2, because it closes itself for God knows what the reason on the opening of wbMPA in the Sub_1.
If you have only opened a blank workbook (clicking Excel from Toolbar, for example) and then you open any named workbook before making any changes to the blank workbook, the blank workbook will disappear. I believe that is normal/expected behavior.
I can't speculate why this happens on one computer but not another, but this is always how I have observed new/blank documents (Excel, PowerPoint, Word) to behave, and assume this to be the normal behavior. You may have some different option/configuration on your Excel environment which is changing this default behavior, or maybe you are slightly altering the blank file before running the macro, and your co-worker isn't, etc.
A word of caution to avoid relying on ActiveWorkbook -- and especially in this case if the expectation is to always Set dwb to a new/blank workbook, the best way to do that is to explicitly create a new/blank workbook, rather than relying on the user to manually open a new/blank target workbook.
Set dwb = Workbooks.Add
If, on the other hand dwb must be assigned to some other known/existing workbook, then you should be either providing the file path to an Open statement, or the workbook name to the Workbooks collection.
On a related note, it's almost never necessary to Activate a workbook, see here:
How to avoid using Select in Excel VBA macros
And further note: your variables aren't globally scoped, they're scoped only to the module using Dim statement. A public declaration uses the Public keyword, not the Dim keyword. Both module-scoped and global-scoped should be used with caution (Public moreso than module-scoped) and in most cases it's preferable to pass objects/variables by reference to dependent subs and functions:
How to make Excel VBA variables available to multiple macros?

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.

Open new workbook, and run macro, using button

I have a workbook that runs a large multitude of macros, all based on which button is clicked.
I need one of the macro to open a new workbook, and import two files into the workbook (that part I can do).
The second part is that I need it to run a particular macro, in the new workbook. The macro would be stored in the original workbook.
I've seen some suggestions that I need to use APPLICATION.RUN, which has the parameters of having to choose a workbook and name of the routine.
Does this method work?
Assuming it does:
For the workbook, do I need to specify the file path, or just the workbook name?
For the macro, do I need to make the macro public?
Do I need to specify the module it is in?
Are there any other parameters I need to specify to get this method to work?
Is there another method that might work, if APPLICATION.RUN does not work?
What you need to use is :
Application.Run "'FileName.xlsm'!MacroName", "Parameters"
You don't need to specify the path if your workbook is already open, you don't need to specify the module as you can't have doubloons names for different procedures, and there are no other parameters needed.
The procedure doesn't need to be public (let me know if it does), and for alternative methods in VBA, there is none (as the Call method only work in the same workbook), but there are some in VB if this doesn't work out.
Here is a very short example that you can adapt:
Sub demo()
Dim Original As Workbook
Dim Created As Workbook
Set Original = ThisWorkbook
' create a new workbook
Workbooks.Add
Set Created = ActiveWorkbook
' go back
Original.Activate
'make a change in the newly created workbook
Created.Sheets("Sheet1").Range("A1").Value = "whatever"
' save the newly created workbook
Created.Save
Created.Close
End Sub

What's the difference between module and a module sheet?

I have three modules (standard module, not class module) in my Excel VBA project. One of those was added by right-clicking the VBAProject and insert a module. Two of them were added by executing "modules.add"; I guess modules means module sheets, right? So, my questions are:
1. What does a module mean in VBA?
2. How can I know which module is created while adding a modulesheet?
3. Why when I add a worksheet,there isn't a module appear?
A module is simply a place to put your code. You can just see it as a sheet of paper where you can write something.
In Excel you can put your code in a module, or "behind" a worksheet (what you call a modulesheet). A module is always added manually BY YOU. The "modulesheet" is part of a sheet and thus added or deleted BY EXCEL automatically whenever you create or remove a sheet
When you put some code and variables in a module, its instantly available from all worksheets within your workbook, dependinv on how you declare your variables, subs and functions (private/public)
Usually when one put code behind a worksheet, its because this code is only meant to be called from that particular worksheet and only perform operations on it. I personnaly never work on worksheet level for several reasons:
if you delete your sheet, you lose all code that was behind it.
if you export a sheet, you export the code as well.
if you duplicate a sheet, you duplicate the code as well.
if you want to run the code behind a particular worksheet from another place, you have to fully qualify your calls and variables, which is a horrid solution and also plain boring
My advice is: IN EXCEL, never work on sheet level, uses modules, and if you reach a decent amount of code, organise it in logical groups using multiple modules
RE 1: A module is like a singleton class which has already been initialized, so to speak. In other words a module's code (public properties/variables, public methods, etc) is always available, one does not have to inherit an instance before tries to use it, however, all the data in a module's properties/variables are common "application-wide"
RE 2: It is "sheet module", not "module sheet". This means nothing but the newly added sheet has a module with it when you add it. There's no difference between having this, or adding a module to the sheet later. You can open the code editor window and see modules, classes, etc by pressing ALT+F11
RE 3: By default no VBA code is added to any new sheet because of security reasons. If a Workbook has macro (VBA code) in it, extra attention is required so by default a workbook is not a VBA(macro) workbook.Last but not least, please note that there's no difference between one module or another. The way you create a module does not affect the way modules behave, and there's only one "type of module" (as well as one "type of class module") in Office applications (Excel, Word, etc)
for number 2:
Sub moduleAdd()
Modules.Add
'Modules.Add
qq = Modules.Count
MsgBox "total added module by code = " & qq
For ii = 1 To qq
Set modu = Modules(ii)
If ii = qq Then MsgBox "Newly added module = " & modu.Name
Next
End Sub

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.