Outside code for Excel 2003 macros - excel

Does someone know how to write VBA code in outside file, which is "imported" into Excel 2003 VBA macro every time you run this macro in Excel - like self updating code?
The idea is I write code in outside editor (VIm), and every time I start Excel macro, macro checks for newer data, import that code inside if necessary and then run it (may be one or more functions/subs).
The steps are: open Excel, open XLS file, click on the button, the macro starts, checks for newer outside data (code I have written in outside editor), if data is new, delete old code, import outside code and then actually run macro itself.
Anyone?

Assuming your using this VIM (a text editor) to write .bas files, you can then use the many, many functions for overwriting modules or procedures from this site Programming the VBE.
So, your structure would like something like this:
Sub MyMacro()
Overwrite MyModule in this Workbook with MyModule in .bas file (you could do line count checks or something if you need to, or just overwrite each time to ensure latest code
Call MyModule
End Sub
There are obvious checks you want to incorporate, I would think, to ensure that your latest code always works with the workbook...
checks for newer outside data (code I have written in outside editor), if data is new, delete old code, import outside code and then actually run macro itself.

Found solution (partly from http://www.cpearson.com/excel/vbe.aspx) 10x Scott:
Sub AddOutsideCode()
Dim VBProjekt As VBIDE.VBProject
Dim VBKomponenta As VBIDE.VBComponent
Set VBProjekt = ThisWorkbook.VBProject
Call DeleteAllButThis(ThisWorkbook, "Fixed")
Set VBKomponenta = VBProjekt.VBComponents.Import(Filename:="C:\bat\bat.bas")
VBKomponenta.Name = "OutsideCode"
End Sub
'Delete all modules but named
Private Sub DeleteAllButThis(ByVal wb As Workbook, ByVal CompName As String)
Application.DisplayAlerts = False
On Error Resume Next
For Each komponenta In ThisWorkbook.VBProject.VBComponents
If komponenta.Name <> CompName Then
If komponenta.Type = vbext_ct_StdModule Then
ThisWorkbook.VBProject.VBComponents.Remove komponenta
End If
End If
Next komponenta
On Error GoTo 0
Application.DisplayAlerts = True
End Sub
But how to check if the outside code changed or not?

Related

Is it possible to prevent code available in personal.xlsb?

I've been self-teaching myself VBA for a couple of years now (working on it on and off to make my work life easier). I'm usually able to find answers to my questions on this forum, but not this one.
I have a lot of 'example' code in my own personal.xlsb. For instance I have a public sub with a modified messagebox with standard caption etc I always like to make visible, referred to as PublicSub_MyMsgBox
Often, the automation I create, will be used by my colleagues as well. I would create a specific workbook with specific buttons for a specific goal. I would copy the required code into a module in this shared workbook. The workbook would therefore have a module with the sub PublicSub_MyMsgBox as well.
Another sub in this shared workbook will have a line to call PublicSub_MyMsgBox.
How can I be sure that when I test a code created in this shared workbook, it does not use the PublicSub_MyMsgBox from my own personal.xlsb, but actually only uses the code from the workbook?
(to be able to verify that I have indeed copied all relevant code into the workbook for my colleagues to use as well).
Thank you very much for your help!
Linda
No code from personal.xlsb will run even if a reference to pers. workbook exists! You can be sure that no code from personal.xlsb will be run, instead of the one inside the workbook where the call is made.
Even if you added a reference to your personal.xlsb, for identic macro names VBA will choose the one in the workbook where the code runs. To check that, please, try the next scenario:
Create in your personal workbook the next macro:
Public Sub MyMsgBox()
MsgBox "Hello from Personal workbook!"
End Sub
Then create the next macro in a standard module of your new workbook:
Sub MyMsgBox()
MsgBox "Hello from This workbook!"
End Sub
Create a simple checking Sub, of course, in the new workbook, add a reference to your personal.xlsb workbook and run it:
Sub testMyMsgBox()
MyMsgBox
End Sub
It will run the Sub in your workbook. Now, comment the Sub from your workbook and run testMyMsgBox again. It will ran the procedure from personal.xlsb...

Run another macro in another Excel file

Hi so I am building an Excel system to run macro for running several different Macros on other Excel files.
Let's say I have two Excel files: "Parent.xlsx" and "child.xlsx" opened already.
In my Parent.xlsx I am running an VBA script, how can I run a macro called "method1" in my "PERSONAL.XLSB" for my "child.xlsx"
Right now, in my Parent.xlsx, I try to run this macro VBA script:
Workbooks("child.xlsx").Application.Run "PERSONAL.XLSB!method1"
In my PERSONAL.XLSB in Modelue6, I have:
Sub method1()
Dim rTable As Range
Selection.AutoFilter
End sub
Error:
Run-time error'1004':
AutoFilter method of Range class failed
Thank you very much!
You need to qualify and reference the workbook that the code should act on.
Look into the ActiveWorkbook property here: https://learn.microsoft.com/en-us/office/vba/api/excel.application.activeworkbook
Application.Run
In Parent.xlsx (to keep the code, save the file as e.g. Parent.xlsm)
Sub callMethod1()
Dim wb As Workbook
Set wb = Workbooks("child.xlsx")
Application.Run "PERSONAL.XLSB!method1", wb
End Sub
In PERSONAL.XLSB
Sub method1(wb As Workbook)
wb.Activate
If Not Selection Is Nothing Then
If TypeName(Selection) = "Range" Then
If ActiveSheet.AutoFilterMode Then
ActiveSheet.AutoFilterMode = False
End If
Selection.AutoFilter
End If
End If
End Sub
First off, you can't expect macros in a workbook of xlsx format. Make sure that you have eikther xlsm or xlsb as a source. This gets us to the second necessity. The workbook must have been saved to have such a format. You can't call a macro from an open workbook that hasn't been saved (because it doesn't have any format yet).
This is the correct syntax for calling an existing macro in another workbook.
Sub Test_TestCall()
Application.Run "'Personal.xlsb'!Method1"
End Sub
Add arguments to the call in brackets in the sequence required by the called procedure.
You may find it easier, however, to simply set a reference to the other workbook. Here is a step-by-step guide how to set a reference. Once a reference is set you can call all macros in the other project as if they were within the calling workbook. The reference gets saved with the workbook and will still be there when you next open the workbook. The drawback of this (and any other method) is that you can't send a working copy of the calling workbook to third parties unless you send the referenced workbook as well.
The error you get has yet another reason. The Selection is made by the user in the ActiveWorkbook, and since you don't tell Method1 which workbook is active it wouldn't be able to find it, right? However, this problem is best solved by following the most basic of all programming rules: "Avoid the Selection object!" Use the Range object instead. If you absolutely must use the Selection` object then pass it to your procedure as an argument.
Application.Run "'Personal.xlsb'!Method1(Selection)"
' and
Sub method1(MyRange As Range)
Dim rTable As Range
MyRange.AutoFilter
End sub

VBA Code to delete macro automatically opening when the file opens

I have a master file that opens a user form automatically when started, lets users select options and then saves the file as a new version modified based on these selections. The code that initiates this action is assigned to ThisWorkbook Object. I would like this version not to contain the macro that opens the form at the beginning. Is there a way to erase this part of the code when the new version is being saved?
The easiest way to do this is to perform a check before running any code. This won't strip out the code, it will just stop it from running.
The check could be the name of the workbook or a value in a cell
eg
Private Sub Workbook_Open()
If ThisWorkbook.Name <> "MyMacroWorkbook.xlsx" Then Exit Sub
'current code here
End Sub
or
Private Sub Workbook_Open()
If ThisWorkbook.Worksheets("HiddenWorksheet").Cells(1, 1).Text <> "RunMyMacro" Then Exit Sub
'current code here
End Sub
The other method is to modify the VBA code, but this is dangerous and normally blocked in most organisations as it is potentially very dangerous.

Excel-VBA Import Module from text file (without requiring trust center)

I'm trying to create a custom library of subs and functions saved as .txt files in a network location where various users of the workbook I'm creating can import them based on which userform function they select. The users of the workbook are only going to be using the workbook through the userforms. I don't want to require them to modify their security trust center settings for this import libraries code to work, so I don't want to use the wb.VBproject.References.import command, or make them add-in.
I found this method which works great, but only as long as no other workbooks are open. If another workbook is open, this code ends up inserting a new module in that other workbook instead, and then the userform calls are meaningless.
I don't understand how this is possible, since I'm referencing all of this code within a "With ThisWorkbook" statement. What am I doing wrong?
Sub importLib(libName As String)
Application.DisplayAlerts = False
Path = "C:\Users\(username)\Desktop\excelLibraries\" 'Example only, my path is actually a network location
lib = Path & libName
Dim wb As Workbook
Set wb = ThisWorkbook
With wb
On Error Resume Next
Dim wbModules As Modules
Set wbModules = .Application.Modules 'wb=ThisWorkbook, but doesn't point to this workbook when other workbooks open???
'wb.VBProject.VBComponents.Import lib '---> library saved as .bas, but this requires the user to change their security settings
'wb.VBProject.References.AddFromFile lib '---> library saved as add-in reference, but this requires user to change security settings
'----This method works when no other workbooks are open
For Each a In wbModules 'Clear any previous Library Module
If a.Name = "Library" Then
a.Delete
Exit For
End If
Next
Set m = wbModules.Add 'Create a new Library Module
m.Name = "Library"
m.InsertFile lib 'Insert the code
On Error GoTo 0
End With
Application.DisplayAlerts = True
End Sub
Sub callsub()
importLib "library1.txt"
End Sub
Making the following changes seemed to work for me.
Change Dim wbModules As Modules to Dim wbModules. Otherwise I get a type-mismatch error.
Remove the .Application in Set wbModules = .Application.Modules.
EDIT:
As #Mathieu Guindon has pointed out in a comment, but worth preserving in the question body, Application refers to the Excel application instance, the host. The Modules collection contains all open modules, regardless of the workbook.
Try referencing the following through the VBA editor
Tools > References > Microsoft Visual Basic For Applications Extensibility 5.3
This will allow the code to remove "Library" then reimport the new code.

Excel's VBA ActiveWorkbook is "Nothing" when "Enable Editing" from ProtectedView

I have a VBA macro which is called from a spreadsheet function (user defined function, UDF). When the spreadsheet is downloaded from the internet and the user has set "Trust Center" settings accordingly, the spreadsheet will open in so the called "Protected View". The function will not be called. A button "Enable Editing" is shown.
If the button is pressed, the spreadsheet is "trusted" and reopened normally, starting calculation, and hence calling the user defined function.
However, in that VBA function the value of Application.ActiveWorkbook is Nothing. This can be verified in the debugger.
Since I just need to read some properties (like path name) of the spreadsheet, I could alternatively inspect the availability of Application.ActiveProtectedViewWindow which should reference to the protected version of the workbook. In the debugger, this object can be inspected. However, running in release (without debug) the value of Application.ActiveProtectedViewWindow is also Nothing.
Both behaviors - especially the first one - appears to be a bug present in Excel 2010 and 2013 (see also a post at the MSDN forum ).
Question: Is there a way to get hold of properties of the active workbook after it has been enabled for editing?
PS: As a follow up to the nice observation of Siddharth Rout, that "ThisWorkbook" might work: In my case, the macro is not part of the Workbook being openend. The UDF is defined in an XLA. Hence, ThisWorkbook would reference the XLA. I do need to get the ActiveWorkbook (= the workbook calling the UDF) instead of ThisWorkbook (= the workbook running the UDF).
IMPORTANT REQUIREMENT:
My function is called as a user defined function, i.e., execution order is determined by Excel updating the cell.
The function is not part of the workbook being opened. It is part of an XLA.
I cannot add any code to the workbook which is opened.
Summary: The problem can be replicated and there are some possible workarounds. The most promising one - resulting from a chat - is to use ActiveWindow.Parent instead of ActiveWorkbook.
I was able to replicate the problem.
I tried
Private Sub Workbook_Open()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ActiveWorkbook Is Nothing)
End Sub
And I got True
However, then I tried this and it gave me False
Private Sub Workbook_Open()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ThisWorkbook Is Nothing)
End Sub
Now answering your question...
Question: Is there a way to get hold of properties of the workbook after it has been enabled for editing?
Yes. Use ThisWorkbook instead of ActiveWorkbook
Followup From Comments
Once the workbook completely loads after you exit the Protected Mode, you would be able to access the ActiveWorkbook object. To test this, put this code in the protected file.
Private Sub Workbook_Activate()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ActiveWorkbook Is Nothing)
End Sub
You will notice that you get a False
So once your workbook loads, your add-in can use ActiveWorkbook to interact with the opened file.
Here is another test
Private Sub Workbook_Activate()
MsgBox ActiveWorkbook.Path
End Sub
This is what I got the moment, I exit the Protected Mode
FOLLOWUP FROM CHAT
Using ActiveWindow.Parent.Path instead of ActiveWorkbook.Path would solve the problem.
I had this same issue today, and neither the accepted answer nor any other answer that I could find on this page or through searching the Google-verse worked for me. I'm using the version of Excel within Office 365, and I figured that was at the root of the problem.
I eventually came to a solution after finding a Microsoft Excel 2010 resource and hitting the old try-fail cycle for a few hours. Here's what I got:
Option Explicit
Public WithEvents oApp As Application
Private bDeferredOpen As Boolean
Private Sub Workbook_Open()
Set oApp = Application
End Sub
Private Sub oApp_WorkbookActivate(ByVal Wb As Workbook)
If bDeferredOpen Then
bDeferredOpen = False
Call WorkbookOpenHandler(Wb)
End If
End Sub
Private Sub oApp_WorkbookOpen(ByVal Wb As Workbook)
Dim oProtectedViewWindow As ProtectedViewWindow
On Error Resume Next
'The below line will throw an error (Subscript out of range) if the workbook is not opened in protected view.
Set oProtectedViewWindow = oApp.ProtectedViewWindows.Item(Wb.Name)
On Error GoTo 0
'Reset error handling
If oProtectedViewWindow Is Nothing Then
bDeferredOpen = False
Call WorkbookOpenHandler(Wb)
Else
'Delay open actions till the workbook gets activated.
bDeferredOpen = True
End If
End Sub
Private Sub WorkbookOpenHandler(ByVal Wb As Workbook)
'The actual workbook open event handler code goes here...
End Sub
The difference between the 2010 solution and mine is that I had to call Workbook_Open and explicitly set the oApp variable there, because without that assignment neither the oApp_WorkbookActivate nor oApp_WorkbookOpen functions would fire when I opened the file.
Figured that someone else might be able to benefit from this, so I posted it, despite the fact that the most recent update to this thread is better than 2 years old.
Best.
Try using Application.Caller.Parent.Parent instead of Application.Activeworkbook
This is not a complete answer to the original question, but a (dirty) workaround for a problem related to this.
I needed ActiveWorkbook to infer the workbooks path, that is ActiveWorkbook.Path.
An alternative to using ActiveWorkbook.Path is to check for Application.RecentFiles(1).Path which is the path of the most recently opened file. In many cases this will be the workbook for which the user just has "Enabled Editing". However, of course, this method may fail: In the case the used opened another sheet, then enabling the previously opened sheet.
(Note: ActiveWorkbook.Path give the path of the folder, while Application.RecentFiles(1).Path gives the complete path of the file, so there has to be some post-processing).
I know it's old thread, but i came across the same issue and i found solution ;)
The only way to go around it, is to use variable type Workbook
Dim wbk as Workbook
Set wbk = Application.ProtectedViewWindows(index).Workbook
Warning:
ActiveSheet returns Nothing when active window is protected too.
Dim wsh As Worksheet
Set wsh = wbk.Worksheets(index)
Try this code it works.
If (UCase(ActiveWorkbook.Name) = ucase("<YOUR XLA NAME WITH EXTENSION>")) Then
End
End If
Set wbObj = ActiveWorkbook
First time when you run the macro, it just ends without doing anything. Second time it picks up the proper file.

Resources