VBA execute code stored in Cells - is it possible? - excel

Is it possible to execute code written in specific excel cells?
For example: In Range("A1") I have text -> debug.print("Test"). I need some way to execute this from VBA code.
Is it possible?

First, use this answer to ensure programmatic access to the VBA model. Then, use this method that makes a string parser for a simple command located in a worksheet cell. This should be enough to get you going.
Demo:
Option Explicit
Sub test()
Dim strCode As String
strCode = Sheet1.Range("A1").Text
StringExecute strCode
End Sub
Sub StringExecute(s As String)
' Credits to A.S.H., see https://stackoverflow.com/questions/43216390/how-to-run-a-string-as-a-command-in-vba
Dim vbComp As Object
Set vbComp = ThisWorkbook.VBProject.VBComponents.Add(1)
vbComp.CodeModule.AddFromString "Sub foo()" & vbCrLf & s & vbCrLf & "End Sub"
Application.Run vbComp.Name & ".foo"
ThisWorkbook.VBProject.VBComponents.Remove vbComp
End Sub

No. You can't store code in the worksheet. But you can store the name of a procedure in a worksheet cell and then call that procedure using Application.Run

Short answer: No
You can only run code that is within the VBA editor. Therefore it is not possible to run code from a cell directly.
But …
But what you can do (as a workaround) is writing a procedure that extracts the VBA code from that cell and includes it into a module and then runs that code.
However even if this is possible this would be a task for a pro-user and it is not recommended for beginners (you should know what you do here).
Nevertheless if you want to have a look into how to add procedures/functions into the VBA Editor have a look at Programming The VBA Editor.

The closest you can easily get to your goal is to use VBA User Defined Functions. These can be called from worksheet formulas in the same way as native Excel functions.
But they have limitations compared to a VBA subroutine - the main one being that they can only return information to the cells that they occupy.

Related

Possible to write Code from cells to VBA?

I was wondering how I would call something in VBA to write its code while running? So I mean if I had the text in A1 read:
sub Write()
Call OtherScript
End Sub
So again that is text inside the cell not in a VBA script. And then in a script while its running it Calls "A1" and the code that's in A1 gets run through VBA without having to actually put it in there.
This is not a real code obviously, I am really just trying to find out if this is possible. A friend that helps me learn to code and works me through a lot of VBA's said he does not know how that would work so Im posting it here to see if possible.
Please, try the following code. Before running it, write in a cell:
sub WriteSomething()
Call OtherScript
End Sub
You cannot create a function/sub named Write because this word is illegal, meaning something else in VBA.
and in the next cell (on the same row):
sub OtherScript()
MsgBox "Hello!"
End Sub
I used "K2". Use it too, or adapt the range from the code. You should also have a Module3 standard module. Please, update the module name with the one existing in your vbProject. Anyhow, the code can also create the module...
Copy the next code and run it:
Sub write_Run_Subs()
'It needs a reference to 'Microsoft Visual Basic For Applications Extensibility x.x'
Dim vbProj As VBProject, objMod As VBComponent, mdlName As String
Dim rngStr As Range, strSub1 As String, strSub2 As String
Set rngStr = Range("K2")
strSub1 = rngStr.value
strSub2 = rngStr.Offset(0, 1).value
mdlName = "Module3" 'of course, it have to exist in ThisWorkbook vbProject
Set vbProj = ThisWorkbook.VBProject
Set objMod = vbProj.VBComponents(mdlName)
objMod.CodeModule.AddFromString strSub1
objMod.CodeModule.AddFromString strSub2
Application.Run mdlName & ".WriteSomething"
End Sub
It is only a simple code without too much error handling, but it should work... If you run it twice, it will insert two such subs, if not preliminarily check their existence.
If adding the necessary reference looks complicated, please firstly run the following code, which will add it:
Sub addExtenssibilityReference()
'Add a reference to 'Microsoft Visual Basic for Applications Extensibility 5.3':
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:="{0002E157-0000-0000-C000-000000000046}", _
Major:=5, Minor:=3
End Sub

Non-Basic Code in VBE Call Stack - What Does it Mean?

I am just now learning about how to use the Call Stack within the VBE. One question I have that I cannot seem to find an answer for is what it means when, as I am stepping through two subroutines, why there are two instances of [<Non-Basic Code>] appearing in my Call Stack.enter image description here
I am not sure if it is relevant, but I have included my two subroutines I am stepping through if it helps!
Here are my module-level declared variables (with the exception of Option Explicit):
Option Explicit
Dim fso As Scripting.FileSystemObject
Dim NewFolderPath As String
My first subroutine:
Sub UsingTheScriptingRunTimeLibrary()
Dim OldFolderPath As String
NewFolderPath = Environ("userprofile") & "\Documents\Education\Excel\VBA\Test" 'where we I am copying TO
OldFolderPath = Environ("userprofile") & "\Documents\Education\Excel\VBA\Excel VBA Introduction - Wise Owl" 'where we are copying FROM
Set fso = New Scripting.FileSystemObject
If fso.FolderExists(OldFolderPath) Then
If Not fso.FolderExists(NewFolderPath) Then
fso.CreateFolder NewFolderPath
End If
Call CopyExcelFiles(OldFolderPath) 'since we are sending this as an argument to our CopyExcelFiles sub for its StartFolderPath parameter, we do not need to set it in our next sub
End If
Set fso = Nothing
End Sub
Here is my second sub:
Sub CopyExcelFiles(StartFolderPath As String)
Dim Fil As Scripting.File
Dim SubFol As Scripting.Folder
Dim OldFolder As Scripting.Folder
Set OldFolder = fso.GetFolder(StartFolderPath)
For Each Fil In OldFolder.Files
If Left(fso.GetExtensionName(Fil.Path), 2) = "xl" Then
Fil.Copy NewFolderPath & "\" & Fil.Name
End If
Next Fil
For Each SubFol In OldFolder.SubFolders
Call CopyExcelFiles(SubFol.Path)
Next SubFol
End Sub
Anyways, when I am stepping through my first subroutine, I see my first subroutine listed and one instance of (and this is the literal text) [<Non-Basic Code>]. However, when I begin stepping through my second subroutine, I see two instances of [<Non-Basic Code>] as well as both of my subroutines listed.
Any help on this would be greatly appreciated!
As freeflow mentions in comment to you, the call stack shows [Non-Basic Code] whenever an external code is called.
Look at this part:
For Each Fil In OldFolder.Files
...
Next Fil
You are doing a For Each over the OldFolder.Files; who is the one doing the enumerating? It's not obvious when you program VBA, but if you were writing C, something has to implement the enumerator for the For Each statement to work on. In this case, the Scripting.Files object has implemented a hidden enumerator. Thus, when VBA has a For Each, it's calling the hidden enumerator, and getting a item out of it.
This is not the only way. There are other ways you can see a Non-Basic Code -- a common routine is via event handlers. Say you do a MyWorkbook.Save to save the Excel workbook. That will call events on the workbook such as BeforeSave event and so forth. If you have VBA code in the event handlers, you certainly will see a Non-Basic Code between the calls.
Basically, whenever your code has to pass through some external library to execute the code, the call stack will insert Non-Basic Code to let you know that there's a layer between your VBA code and the previous code that triggered it. There are also cases where your VBA code might be accessed by a non-basic Code entirely so it'll be the first one in the call stack, too.

Forumula to create Table of Content in Excel

I am looking for a formula which can directly be used in cells to read all the active tabs' name. Please refer the screen shot for the same.
There is also a =MID(CELL("filename"),FIND("]",CELL("filename"))+1,255) formula, but it is only giving the current tab name.
Though this is easily possible using macro, but would be great if can get formula for that.
There is a way to do this through formula's only,
Have a look here
It feels a bit double to post exactly how it's done, but the approach makes use of a named range and a lookup formula
It's fairly easy to do
I note you say formula but you could use a very simple User Defined Function (UDF) which goes in a standard module in the VBE (which you open with Alt+F11)
Option Explicit
Public Function GetTabName(ByVal tabIndex As Long) As String
GetTabName = ThisWorkbook.Worksheets(tabIndex).Name
End Function
The sheet index gets passed into the UDF as a parameter and the associated sheetname is returned.
If testing for visible sheet you could use the following, which has additional handling for sheet not found:
Option Explicit
Public Function GetTabName(ByVal tabIndex As Long) As String
Dim ws As Worksheet
On Error GoTo Errhand
Set ws = ThisWorkbook.Worksheets(tabIndex)
If ws.Visible Then
GetTabName = ThisWorkbook.Worksheets(tabIndex).Name
Else
GetTabName = "N/A"
End If
Errhand:
If Err.Number <> 0 Then
Select Case Err.Number
Case 9
GetTabName = "Sheet not found"
End Select
End If
End Function
UDF Limitations

Macro for formatting before print

I've seen similar questions to this but haven't found a good answer. I am trying to have a macro run automatically that formats the footer date and font before printing.
This code doesn't work, but is close on the date/font formatting:
Sub Fix_Footer_Date()
ActiveSheet.PageSetup.CenterFooter = "&14&""Verdana,Bold" & Format(Now, "mmmm dd, yyyy")
End Sub
And something like this will automatically run it before it prints?
Option Explicit
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Your code here
End Sub
How can I combine these into one that works? Any help greatly appreciated.
First thing is that I strongly encourage you to qualify the sheet you wish to operate on versus using ActiveSheet, as ActiveSheet may not always be the sheet you desire to work with.
Second is, I fixed the syntax to get your Page Footer to work as intended. I simply recorded a macro to get the correct syntax (and adjusted it for your formula).
Sub Fix_Footer_Date()
Dim ws as Worksheet
Set ws = ThisWorkbook.Worksheets("mySheet") 'change as needed
ws.PageSetup.CenterFooter = "&""Verdana,Bold""&14" & Format(Now, "mmmm dd, yyyy")
End Sub
Then, just call the macro in your Before_Print event.
Option Explicit
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Fix_Footer_Date 'you could also write the code directly into this event, if you wish.
End Sub

Modifying a spreadsheet using a VB macro

I have two spreadsheets... when one gets modified in a certain way I want to have a macro run that modifies the second in an appropriate manner. I've already isolated the event I need to act on (the modification of any cell in a particular column), I just can't seem to find any concrete information on accessing and modifying another spreadsheet (this spreadsheet is located on a different LAN share also... the user has access to both, though).
Any help would be great. References on how to do this or something similar are just as good as concrete code samples.
In Excel, you would likely just write code to open the other worksheet, modify it and then save the data.
See this tutorial for more info.
I'll have to edit my VBA later, so pretend this is pseudocode, but it should look something like:
Dim xl: Set xl = CreateObject("Excel.Application")
xl.Open "\\the\share\file.xls"
Dim ws: Set ws = xl.Worksheets(1)
ws.Cells(0,1).Value = "New Value"
ws.Save
xl.Quit constSilent
You can open a spreadsheet in a single line:
Workbooks.Open FileName:="\\the\share\file.xls"
and refer to it as the active workbook:
Range("A1").value = "New value"
After playing with this for a while, I found the Michael's pseudo-code was the closest, but here's how I did it:
Dim xl As Excel.Application
Set xl = CreateObject("Excel.Application")
xl.Workbooks.Open "\\owghome1\bennejm$\testing.xls"
xl.Sheets("Sheet1").Select
Then, manipulate the sheet... maybe like this:
xl.Cells(x, y).Value = "Some text"
When you're done, use these lines to finish up:
xl.Workbooks.Close
xl.Quit
If changes were made, the user will be prompted to save the file before it's closed. There might be a way to save automatically, but this way is actually better so I'm leaving it like it is.
Thanks for all the help!
Copy the following in your ThisWorkbook object to watch for specific changes. In this case when you increase a numeric value to another numeric value.
NB: you will have to replace Workbook-SheetChange and Workbook-SheetSelectionChange with an underscore. Ex: Workbook_SheetChange and Workbook_SheetSelectionChange the underscore gets escaped in Markdown code.
Option Explicit
Dim varPreviousValue As Variant ' required for IsThisMyChange() . This should be made more unique since it's in the global space.
Private Sub Workbook-SheetChange(ByVal Sh As Object, ByVal Target As Range)
' required for IsThisMyChange()
IsThisMyChange Sh, Target
End Sub
Private Sub Workbook-SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
' This implements and awful way of accessing the previous value via a global.
' not pretty but required for IsThisMyChange()
varPreviousValue = Target.Cells(1, 1).Value ' NB: This is used so that if a Merged set of cells if referenced only the first cell is used
End Sub
Private Sub IsThisMyChange(Sh As Object, Target As Range)
Dim isMyChange As Boolean
Dim dblValue As Double
Dim dblPreviousValue As Double
isMyChange = False
' Simple catch all. If either number cant be expressed as doubles, then exit.
On Error GoTo ErrorHandler
dblValue = CDbl(Target.Value)
dblPreviousValue = CDbl(varPreviousValue)
On Error GoTo 0 ' This turns off "On Error" statements in VBA.
If dblValue > dblPreviousValue Then
isMyChange = True
End If
If isMyChange Then
MsgBox ("You've increased the value of " & Target.Address)
End If
' end of normal execution
Exit Sub
ErrorHandler:
' Do nothing much.
Exit Sub
End Sub
If you are wishing to change another workbook based on this, i'd think about checking to see if the workbook is already open first... or even better design a solution that can batch up all your changes and do them at once. Continuously changing another spreadsheet based on you listening to this one could be painful.

Resources