How to make 'Workbook_WindowActivate' be called when I'm using dual display? - excel

This trigger(and Workbook_WindowDectivate) can successfully be called when I switch window from a workbook to another workbook IN THE SAME SCREEN, but when I switch to a workbook in another screen, it doesn't work.
It seems they belong to 2 different processes, I can find there are 2 Excel.exe in task manager.
I have a lot of datas and a lot of slicers in one of my workbook which make my sheets run very slowly, but I need keep the global setting 'AutoCalculate' on.
So I'm trying to use a macro to desable it's calculation.
I frequently open multiple windows in 2 screens when working, so my intent is to make this macro can be called when I focus on a workbook in another screen.

Assuming there are two distinct processes, you can't.
It's two distinct instances of Excel.Application, each with their own Workbooks collection: you could call one instance "Vegas", and say "what happens in Vegas, stays in Vegas".
Unless... unless your code completely owns that other instance:
Private WithEvents OtherExcel As Excel.Application
Private Sub Workbook_Open()
Set OtherExcel = New Excel.Application
OtherExcel.Visible = True
End Sub
Private Sub OtherExcel_WindowActivate(Wb As Workbook, Wn As Window)
End Sub
Private Sub OtherExcel_WindowDeactivate(Wb As Workbook, Wn As Window)
End Sub
...with a number of caveats:
It opens a new Excel instance every time
It won't handle a 3rd instance
If the execution context dies, the OtherExcel object is gone
Execution context dies whenever the VBE decides a code change requires recompiling
Or whenever an unhandled error occurs
Or when an End statement is encountered
If the OtherExcel instance is closed the object is gone; if another instance is then started, the OtherExcel object is still gone.
You could work around these limitations, by exposing a macro that re-assigns OtherExcel on demand. "Attaching" the OtherExcel reference to an already-running 2nd instance of Excel could probably be done (GetObject comes to mind), but might be unreliable or tricky.

Related

Err.Raise only works in module, but not a sheet or workbook

I'm learning error handling and I think I'm getting the hang of it. But I've come across an interesting behavior. I'm handling my errors in my class modules, and I bubble the errors up the stack. I have traceability so when the error reaches the caller (or top of stack) an error is displayed telling me where it occurred at. It works just fine if the class is being called from a module. BUT if the class is being called from the sheet or workbook, then it generates an unhandled runtime error.
For example, take the following code:
sub testError
err.raise -1000,,"This is a custom error"
end sub
If I run this in a regular module I get the following:
But if I run that exact same code in a sheet or workbook, I get the following:
So some of my classes are being called from macros located on a sheet. Others are being called from workbook and/or sheet events. And if a handled error happens down the line, I get an unhandled runtime error without any useful information.
I can move my macros to a standard module. But I can't move my events (value changes, new caluclation, etc) to a standard module.
So I have two questions. Why can't I do err.raise in a sheet / workbook?
What should I do instead?
Thanks.
So digging into this further. There are two kinds of error popups VBA will give you:
One with a continue/end/debug/help button
One with an okay/help button.
It turns out you can only modify the description with the popup that has the end/debug button. (via the err.raise).
What determines which popup you see is where the top of your stack is. If its in a standard module, you'll get the "end/debug" popup. If its in a sheet/workbook object, then you'll get the "okay/help" popup.
Unhandled errors bubble up all the way to the top stack. If that stack is in a standard module, then you'll have the option of pressing the debug button, which will take you to the line that you're error occurred. If the top stack is in a sheet/workbook, you'll have no idea where it happened.
To illustrate my point, take the following code that will throw a standard runtime error:
sub testError
dim a as long
a = clng("X")
end sub
If this method is ran in a standard module you'll get this:
If you run this in a sheet / workbook you'll get this:
The later doesn't really work well with err.raise (you can't edit the description).
My problem is I have macros and events that are initiated in the sheets / workbooks. Those methods then call other methods in a module. But since the top stack is located in a sheet / workbook, the wheels come off of my error handling.
So my work around is to initiate my module level methods in a way that puts those methods at the top of the stack, and then it works:
'standard module
sub createClass
dim myObj as myClass
set myObj = new myClass
call myObj.raiseError
end sub
Instead of calling this method from the sheet / workbook as so:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
call createClass
End Sub
Do this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
application.run "createClass"
End Sub
Doing it this way will place the createClass method at the top of the stack. and since this is in a standard module, the error handling will work as expected.

UserInterfaceOnly = True is discarded when opening a file

I have an Excel+VBA file named "Myfile.xlsm" which needs that UserInterfaceOnly = True property so the VBA code can perform many operations without the user being aware.
I did place the following code to set UserInterfaceOnly = True property in the Workbook_Open event (also tried in the Workbook_Activate event) of "Myfile.xlsm".
Private Sub Workbook_Open()
Dim WSh As Worksheet
For Each WSh In ThisWorkbook.Worksheets
WSh.Protect Password:="myPWD", UserInterfaceOnly:=True
DoEvents
If WSh.ProtectionMode = False Then MsgBox "UIFOnly Init Failed on " & WSh.Name & " !!"
End If
Next WSh
End Sub
In some cases the UserInterfaceOnly is not set as expected and I get the "UIFOnly Init Failed on " message at opening "Myfile.xlsm" and as a consequence many other problems later.
EVEN BETTER: I created a new very simple "UnlockUIFOnly.xlsm" file which has the same code in Private Sub Workbook_Open().
If I open "UnlockUIFOnly.xlsm" first, protection works as expected. If I then open "Myfile.xlsm" (keeping "UnlockUIFOnly.xlsm" open) it works as expected.
If I close both files and open ONLY "Myfile.xlsm" it fails again.
I'm running Office 365 (version 2203 Build 16.0.150028.20512) 32 bits with Windows 10.
FURTHER OBSERVATION #1: I copied this not working "MyFile.xlsm" from its original directory to a new one.
When I opened it from its new directory, I got a banner to enable contents, and I answered yes. Then everything went smooth (No UIFOnly Init Failed on " message, no error 1004 ...). I checked some functionalities, so Excel asked whether I wanted to save changes, answered yes, then later if I wanted that document to be a trusted document, answered Yes as well.
At second open, I did not get any banner to enable contents (normal) but the problems were back.
FURTHER OBSERVATION #2: I again copied this not working "MyFile.xlsm" from its original directory to a new one.
When I opened it from its new directory, I got a banner to enable contents, and I answered yes. Then everything went smooth (No UIFOnly Init Failed on " message, no error 1004 ...). I checked some functionalities, so Excel asked whether I wanted to save changes, answered YES, then later if I wanted that document to be a trusted document, answered no. All further open tentatives asked to enable contents (answer yes) but were smooth.
It looks like declaring a document as trusted bypasses something or changes in which order events are generated, and has a huge impact on my problem

Cannot run common module in xlam file as macro is disabled

VBA / Excel - 2007
I want to create one (possibly many) .xlam file(s) to hold common modules accessible across projects. Along the way I have received a number of errors but through the creation of a "mickey-mouse" scenario I have boiled it down to one error condition (as shown in actual result):-
I have seen several problems reported here related to this which in the end have either not been answered or the solution has not quite hit the mark
As a precursor to this I was able to put my common module into another .xlsm project and successfully execute it by using a reference to that called project. However it would be preferable not to create a workbook for the sole purpose of housing common modules
'Caller VBAProject (Caller.xlsm):
Public Sub Caller()
Dim i As Integer
i = 0
'*** Error in line below***
Application.Run "C:\Users\IT\AppData\Roaming\Microsoft\AddIns\Common.xlam!Test", i
End Sub
'Called Test (Common.xlam) -- different project, obviously
Function Test(ByRef i As Integer) As Boolean
If i = 0 Then
Test = False
Else
Test = True
End If
End Function
Actual Result
Run-time error '1004':
Cannot run the macro
'C:\Users\IT\AppData\Roaming\Mirosoft\AddIns\Common.xlam!Test'
The macro may not be available in this workbook or all macros may
be disabled.
Steps Undertaken (in Excel Options)
Trust Center
a) Macro Settings
-- both i) and ii) (at different times)
i) Disabled all macros with notification
ii) Enabled all macros
iii) set Trust access to the VBA project object model
b) Add-ins -- left as default ie no option ticked
c) Trusted Locations -- have added the following
C:\Users\IT\AppData\Roaming\Microsoft\AddIns\
Add-Ins
As an Active Application Add-in I have
C:\Users\IT\AppData\Roaming\Microsoft\AddIns\Common.xlam
Can anybody please tell me what I might have missed?
So just to summarise I don't have a direct answer to my question thus far, that is how to avoid the 1004 error when calling a macro within a .xlam file. However, I do have a more than adequate alternative which is to import the common file into a different module within the same project. What I'm realising is that as I write this it isn't an import at runtime or late binding as Zac implied. I'm not sure I'm too worried though (at least at the moment).

Difference between Excel.ThisWorkbook and just ThisWorkbook?

I have some VBA code that ran fine in Excel at my desk at work but at home it's crashtastic. Turns out all I had to do was change
Set shtObj = ThisWorkbook.Sheets(1)
to
Set shtObj = Excel.ThisWorkbook.Sheets(1)
Anyone know why? Our organization has just moved to a SharePoint platform, which I suspect is causing all manner of voodoo to creep up, but I'm just guessing.
Does it work if you change it back to ThisWorkbook?
I suspect it will, and the reason would be because the VBA recompiled itself (and didn't compile properly the first time - hence the propensity to crash).
Recompilation occurs when the version details embedded in the file differ from the version of Office/VBA in use, or there's a change from 32 to 64 bit Office. Editing the line is enough to recompile the line, so adding Excel. before ThisWorkbook was enough to make it recompile. Removing Excel. before ThisWorkbook should force it to recompile again.
The only other thing it might be is if there's a variable named ThisWorkbook, but then I'd expect you to get error 91, "Object variable or With block variable not set", or some other error, but not a crash.
ThisWorkbook is a global-scope Workbook object that you can use to refer to the workbook instance that contains the very VBA code that you're looking at.
So in theory, an unqualified ThisWorkbook is exactly the same object as Excel.ThisWorkbook or Application.ThisWorkbook.
Proof:
Sub Test()
Debug.Print ObjPtr(ThisWorkbook), _
ObjPtr(Excel.ThisWorkbook), _
ObjPtr(Application.ThisWorkbook)
End Sub
Outputs 3 times the same pointer address.
Excel.ThisWorkbook is a member (Property Get) of the hidden Excel._Global module, and Application.ThisWorkbook is a member (Property Get) of the Excel.Application class.
Not sure what's up with your file, but there's not supposed to be any difference between the two.
This appears to be a bug in my particular user/profile. Others are now using the code/workbook with no reports of trouble. Yay!!?

Excel VBA: Can Shell run a macro in a workbook in another Excel instance

For example something like this to open the workbook in a new instance and then start the macro:
Shell(Excel.exe workbook.xlsm!macro1)
Or if the workbook was already open then:
Shell(xlObject macro1)
Most important thing is to use Shell so as to allow the code to continue whilst the macro runs separately. If not possible then perhaps I could Shell(file.bat) to a batch file that would do the same. What would the batch file look like?
Thank you.
You cannot execute VBA code inside a host application from the command line - that includes Shell.
There are two alternatives, though (with Excel#1 being the current open file and Excel#2 being the one you want to run the code in):
Put the code you want to run in the Excel #2 workbook's Workbook_Open event handler so it will execute every time you open that file - independet of how it was opened.
Have Excel#1 create a new Excel.Application object in VBA and open Excel#2 in that application. You could now call the Run() method on the Excel#2's application object to execute code from Excel#2, but this will be done synchronous to the Excel#1's code. You can use the Excel#2 application's OnTime() method though for delayed macro execution, in which case Excel#2's application will call the code when the delay has passed and the code runs asynchronous in Excel#2's application.
Sample code for Option 2 could look like this:
Public Function RemoteRun(ByVal strFile As String) As Application
Dim app As Application
Dim wb As Workbook
Set app = New Application
Set wb = app.Workbooks.Open(strFile)
app.OnTime Now + 1 / 24 / 60 / 60, "RemoteMacro"
Set RemoteRun = app
End Function
Make sure to store the return value (the Excel#2's application object) somehwere so it won't automatically close (it still has to run RemoteMacro asynchronously). Setting its Visible property to True will work as well if you don't want Excel#1's code to manage the lifetime of Excel#2's application object.

Resources