How do I prevent certain macros being run in a Workbook? - excel

I have a reasonably complex group of scripts to perform a certain function for my work. Due to the nature of the work, I have validated the code and have it locked down fairly tightly with passwords to prevent improper use/editing (the script is also locked down, but there are no problems with that for this query). This all works well and if it ever has errors, it exits and re-protects the individual worksheets and the workbook. All macros are run via buttons (actually just objects with macros assigned, not the older style coded buttons) on relevant worksheets and output is good.
However, this morning I discovered that the macros can still be run through the standard Macro screen (Alt + F8). As I have a number of scripts in there that I use for development of updated versions, I want to disable this function.
I did a search for options and found UserInterFaceOnly, but it didn't work for my on Excel 2010 and I am led to believe that it may not be used in later versions.
Is there anyone who knows how to disable the ability for the user to run macros through the Alt + F8 Macro window, whilst allowing macros to be run that are assigned to objects within a worksheet?
The code that locks the worksheets/workbook is as follows:
For Each ws In Worksheets
ws.Cells.Locked = True
ws.Protect Password:=strPWD
Next ws
ActiveWorkbook.Protect Password:=strPWD

I have found a good solution using a different approach. Rather than trying to lock the capability, I have used the 'Option Private Module' entry at the beginning of the module, meaning that the user never gets to see the macros that have been written.
This solution is an obvious one when you think about it, but having not used the Private Module option at all previously, it didn't initially occur to me. Hopefully this answer to my own question will benefit some other people in the future.

Related

How can a workbook reopen itself in a separate Excel instance

I want my workbook to open in its own separate instance of Excel. If it gets opened with other workbooks, it should reopen itself in a new instance, perhaps using a code in the Workbook_Open event. Additionally, the workbook should prevent files from opening in the same instance, perhaps by moving them to a new instance or moving itself to a new one. Any ideas will be much appreciated.
Edit:
The purpose of this is that workbooks loaded with code and userforms tend to crash easily causing all other workbooks open in the same instance to crash and lose all unsaved changes and the end user gets frustrated. Another issue is that when the Application.OnKey is used, I experienced three problems: 1. shortcut triggers a code that's usually intended to act on the workbook it resides in. This can be solved by checking whether the hosting workbook is the active workbook before running, but it's better to prevent them from running in the first place. 2. common shortcuts can be reassigned to run a custom code, but usually this shouldn't affect other workbooks. 3. problems happen when multiple workbooks that assign same shortcut to custom subs. the most recently open workbook takes over the shortcut action. Also, when the workbook that contains the shortcut is closed, using the shortcut reopens the workbook automatically, which is not a desired! Removing the custom shortcut doesn't solve the problem of multiple workbooks using same shortcut.
Therefore, best solution is to start such workbooks in their own application instances.
I've had to make an Excel workbook (which was running a timer) reopen itself in another instance of Excel. This was to let the user use the first instance of Excel without the timer interfering. I'm not sure if that's exactly what you need, but that can certainly help you get started. You can find the code on GitHub here.
The procedure that you would be interested in is called OpenItSelfInAnotherInstance and is located in OpenItself.bas.
For it to work you will also need to include the code contained in API_Maximize.bas, API_Sleep.bas and UDF_ExcelIntances.bas. (If you copy-paste, always exclude the line that says :Attribute VB_Name =... since it's there only for when you import the *.bas file in from the VBE)
Please let me know if there is anything unclear in there, so I can add some explanations in my answer if needed.

Assigning Key to Macro

My real motivation for writing this was Macros Not Showing Up in Macro Table. But the macro question is really just a plain vanilla Sub located in a Module file. Literally:
Public Sub LaunchRecognia()
GetRecogniaFactory.GetRecogniaVm(ThisWorkbook).ShowForm
End Sub
Thinking it might be a security issue but I doubt it (see below).
However the only reason I wanted the dialog was just to assign a shortcut key, so thought I might see if anyone knows how to do this in code while I'm at it.
Any ideas to troubleshoot the macros not being available? Code to assign shortcuts?
Turns out this was a corrupted workbook. NO surprise there but the corruption was difficult to detect!
The workbook had a custom ribbon, with a code module to support it. The code module must have been inadvertently deleted. BUT the code still worked, am surmising that the ribbon must somehow embed the code in binary form.
Moved all sheets and code to a new workbook, recreated the missing module, and all's well with visible macros.
Still would love to know if there is a way to programatically assign a key code tho...

Excel add-in - get workbook name of "thisworkbook"

I can't get the name of the workbook when I use a add-in.
I'm trying to develop a add-in that runs each time you open Excel and reads the filename of the open file.
If the file name is XCFIL.SKV then do something...
This code should do it, but it doesn't. What am I missing?
The code stops and if I debug and press F8 it works fine, but it won't run on it's own.
Private Sub Workbook_Open()
If ThisWorkbook.Name = "XCFIL.SKV" Then
MsgBox "y"
End If
End Sub
Background:
Based in this statement The code stops and if I debug and press F8 it works fine, but it won't run on it's own. I assume the problem relies on the speed of the processor that is not sync with the code (own experience).
Solution:
Since it is Excel and the problem seems to rely only in the opening of the instance itself, you may use Application wait or any of the other functions for this matter.
Further thoughts:
Life cycle comes to my mind in these kind of scenarios. This web page has a neat Lifecycle diagram of excel instance (attached since I will try to explain the scenario)
As you may see "Application" is the first cycle of the Excel application, followed by "Open" and after that "Workbook" object, it may happen that in this life cycle "worbook" has not been created when "Open" comes to play,hence, you need to wait until excel solves this.

What happens when you close a workbook with code running?

I have a workbook on a network containing worksheet data and housing a generous amount of data, VBA code, forms etc. I developed a custom ribbon available via an add-in that opens this file and executes a macro when a button on the ribbon is clicked.
When the ribbon button is clicked, it executes a callback within the add-in that houses the custom ribbon XML file.
Code within the callback uses Application.Run() to open and execute the much larger network file that holds the worksheet data and macros needed for a lengthy automated process.
If a user cancels operation via the "Cancel" button on one of several forms (or if code successfully completes execution), via Application.Run it passes code execution back to another procedure in the add-in housing the custom ribbon.
This "EndProcedures()" macro in the add-in closes the network file.
The issue I am having is that code execution stops (as if an "End" command had been encountered) as soon as I close the network file which houses the vast majority of the VBA code. Since the user has the ability to cancel operation in the middle of the macro, the procedure call to the "EndProcedures()" macro in the ribbon workbook (the one that code execution began from) is something like #8 in the call stack, with #2-7 being procedures residing in the workbook that is being closed by #8. Since I want to halt code execution and the next line in the "EndProcedures()" macro is simply the global "End" command anyway, this isn't necessarily an issue. However, for the last couple of days I have been wrestling with some cryptic errors that I am thinking may be tied to the fact that I am closing a workbook while it has code running in the call stack. I have been getting "Out of Memory" errors in the VBA editor (though strangely I haven't seen them when the VBA editor is not open), and my thought is that this is due to either:
The code window for the network file simply being open in the VBA editor after the file itself is closed by other VBA code, OR
Perhaps actual memory issues due to object references not being cleared because the file was unloaded, rather than an "End Sub" or "End" line being encountered.
Though I use custom objects extensively in the macros housed in the network file, I am very careful about memory usage with them (which shouldn't even be that much) and don't think that this code is the source of the "Out of memory" errors. The question for now is whether or not closing a workbook with VBA code while code is running in the workbook being closed can cause "Out of memory" errors, and if so what can be done so that once macro execution ends users don't have my macro workbook open to wreak havoc with. Will I really have to code proper "Exit Sub" lines all the way out of the call stack?
This is my first post to StackOverflow, even though I probably learned 50% of what I can do in VBA from here, so please bear with me on this one.
After a few weeks of continuing to play with this, it doesn't appear that exiting code execution in the middle of a call stack involving multiple workbooks/modules was the source of the "Out of Memory" issues that were encountered. Though I haven't been able to pinpoint which edits actually addressed the "Out of Memory" errors, at least the issue has been resolved to my satisfaction.
P.S. - I answered my own question because it was no longer an issue, not because I necessarily pinpointed a resolution. As this was my first post to StackOverflow, if this was not the proper action to take please let me know.

Can't enter break mode at this time

This has been happening increasingly, when I have a sheets.add or sheets.delete in excel VBA. After searching and searching I finally found the official Microsoft support page on it. My question is, does anyone know why I could have stepped through this code just fine over and over, and then all of a sudden it starts doing what Microsoft says it would always do, and is there a way to fix it?
Sub foo()
Sheets.add
debug.print "sheet added" 'breakpoint here
End sub
It's as simple as that. You won't be able to recreate it, because the issue I'm asking about is the fact that it doesn't happen at first. It works just fine over and over then randomly presents the error described in the linked Microsoft support page.
Check if Microsoft Visual Basic for Applications Extensibility is being referenced in the project.
You can check that in the Tools/References Menu on the Visual Basic window of the project.
Referencing Visual Basic for Applications Extensibility prevents the program having its execution suspended:
Excel helps says specifically:
A change was made programmatically to the project using the extensibility (add-in) object model. This prevents the program from having execution suspended. You can continue running, or end execution, but can't suspend execution.
You are unable to step through code when making changes to the project (dynamically eg using InsertLine etc). the code can be run but not stepped through.
Deleting certain objects including ActiveX objects actually changes the VB project. It took me some time to realize that the following line of code prevented the VBE from entering break mode:
Excel.ActiveSheet.DrawingObjects.Delete
If you can identify the code causing the issue, and the order of operations isn't important, move it to the end of your script.
Here are a few suggestions which are not fool-proof,
Firstly, verify that the error does not occur if a breakpoint is not set.
If it doesn't, try a few other things:
From the VBE Debug menu, "Compile VBA Project", it's worth a shot.
Delete the line entirely. Run the code. Then put the line back in and try again with the breakpoint.
Add a DoEvents statement after the Sheets.Add
Use a MsgBox instead of a breakpoint on a Debug.Print. With the message box displayed, attempt to manually break using ctrl+fn+End. (At this point, "breaking" isn't necessary but it would be interesting to see whether you can break this way)
Put a breakpoint on Sheets.Add instead; practically speaking, there's no reason to put the breakpoint on a Print statement if you can just put it on the preceding line.
Are there any Addins? If so, disable all of them and re-enable one at a time, testing to see which one may contribute to the error.
Yet another Excel/VBA glitch.
When it happens to me when I click a button running a macro:
I first try to directly run the macro from VBE,
if it fails, then I put a breakpoint at the first instruction of the macro,
if it still fails, I try both,
or, after clicking the button and breaking on the first breakpoint, I do a single step (SHIFT F8) and then I can let debug run freely as usual (F5).
And so far I don't get this error anymore.
Probably not foolproof either but worth a try.
Ran into the same issue, and (as far as I can tell) the only relevant answer here is Answer 5 (i.e. the one provided by Chrisb).
I've been working with vba for (too many) years now, and never encountered this until I had a project that needed vba to delete ActiveX controls. In my case, the 'ActiveX controls' were a spurious result of data copied in from a web page.
Additionally, there appears to be a way around the issue. Using the following code (versus, e.g. deleting the ActiveX as a shape), seems to circumvent the issue:
On Error Resume Next
ActiveSheet.OLEObjects.Visible = True
ActiveSheet.OLEObjects.Delete
On Error GoTo 0
I say 'appears' and 'seems' above as implementing the above solved the issue for me. However, before I implemented same, I had made other code changes and I have not yet fully regression tested for all possible other reasons the problem was resolved. :)
This has happened to me multiple times and this solution works for me.
Run a different macro within the same VBA project.
Then go back and run the same macro that is causing the pop-up message to appear. The message should no longer appear.

Resources