Run a VBA routine from VBA IDE? - vba

I want to get a list of routines from a VBA project, then run the macros selected by the user.
The image below shows the native "Macros" box. I want to extend this functionality to multiple macros across multiple documents.
I found this link which solves the first part of the problem. Now that I have my list, how do I run a selected routine by name?

Hello and welcome to SO
Below is code sample how to execute VBA macro using code. You need to add some form to select documents and macros for execute. This depends on your implementation.
Sub RunMacroUsingCode()
Dim vbaProjectName As String
vbaProjectName = "InventorVBA"
Dim vbaModuleName As String
vbaModuleName = "m_Tests"
Dim vbaMacroName As String
vbaMacroName = "RunMultipleMacrosTestCall"
Dim vbaProject As InventorVBAProject
For Each vbaProject In ThisApplication.VBAProjects
If vbaProject.name = vbaProjectName Then Exit For
Next
Dim vbaModule As InventorVBAComponent
For Each vbaModule In vbaProject.InventorVBAComponents
If vbaModule.name = vbaModuleName Then Exit For
Next
'Using result is optional
Dim result As Variant
Call vbaModule.InventorVBAMembers(vbaMacroName).Execute(result)
End Sub
Function RunMultipleMacrosTestCall()
Call MsgBox("TEST")
RunMultipleMacrosTestCall = True
End Function

Related

Loop Through List and Run Report

First time postings and I'm far from a VBA expert, but I've managed to stumble most of the way to my desired outcome thanks to boards like this one. I'm hoping to automate one last step and am looking for some assistance.
Background:
I run project reports for our Project Managers every Monday morning. The report template queries several tables in our database and populates all of the appropriate fields. It then creates a copy of itself, saves the formulas as values and saves the report using a naming mechanism capturing data from various fields in the report. All of this works great!
The Issue:
At any given time, I have 80-100 active projects. As it stands, I copy the list of projects to a table on the "Parameters" tab. Then, using data validation, I created a dropdown list on the "Report" tab. I then manually go 1 by 1 through the list to generate the report. Each time I change project number in cell B1, the data refreshes and runs the report for the project. I'm using this code to accomplish that:
Private Sub Worksheet_Change(ByVal Target As Range)
'MsgBox Target.Address
If Not Application.Intersect(Range("b1"), Range(Target.Address)) Is Nothing Then
Call AA_RunAll
End If
End Sub
What I'd like to do is create a macro that will run through each one of the projects on my list and run the report. I'm assuming it's a loop function, but I can't seem to get it to work as I want.
One other consideration to note: it takes 3-5 minutes per report to refresh all the data, generate the report and save it. I'd like to set this to run before I leave at night and have it done in the morning.
Thanks in advance.
Aaron
I think this may be what you are looking for.
Dim DataValidationRange As Range
Dim Str As String
Str = Replace(Range("B1").Validation.Formula1, "=", "")
Set DataValidationRange = Range(Str)
Dim i
For Each i In DataValidationRange
Range("B1").Value = i
Call AA_RunAll
Next
Also if you need to wait for the data to update before calling AA_RunAll, you could use this:
Public Function MyTimer(MyDelay As Double)
Dim MyTimerTimer As Double
MyTimerTimer = Timer
MyDelay = MyDelay + Timer
Do While MyTimerTimer <= MyDelay
MyTimerTimer = Timer
DoEvents
Loop
End Function
Private Sub GoThrough_Dropdown()
Dim DataValidationRange As Range
Dim Str As String
Str = Replace(Range("B1").Validation.Formula1, "=", "")
Set DataValidationRange = Range(Str)
Dim i
For Each i In DataValidationRange
Range("B1").Value = i
WaitForDataToUpdate
Call AA_RunAll
Next
End Sub
Private Sub WaitForDataToUpdate()
Dim RangeToWaitFor As Range
Set RangeToWaitFor = Range("H5")
Dim Str As String
Str = RangeToWaitFor.Value
Do While Str = RangeToWaitFor.Value
MyTimer 1
Loop
End Sub

Workbooks.Add not adding a new workbook

I have an Excel function that populates a dictionary with information from a SQL pull. To help visualize the answer set, I had it currently dumping into a new workbook - and while I don't need to visualize it anymore, I still find it helpful to populate.
The answer set doesn't change unless I myself have done something in the database populating it, so I don't need the function to perform the query every time. Therefore, once the dictionary is populated, I am bypassing the query unless I force it to initialize the dictionary with a refresh parameter.
The module is structured as follows:
Option Explicit
Option Compare Text
Private dProducts As Scripting.Dictionary
------
Function ProdLookup(sValue As Variant, sReturn As Variant, sLookupType As
Variant, _Optional iVendor As Integer, Optional bRefresh As Boolean) As
Variant
If sValue = "" Then
ProdLookup = ""
Exit Function
End If
If sLookupType = "SKU" Then
If (dProducts Is Nothing) Or (bRefresh = True) Then
Call Create_dProdsBySKU
End If
ProdLookup = dProducts(CStr(sValue.Value))(CStr(sReturn.Value))
Exit Function
End If
End Function
------
Sub Create_dProdsBySKU()
Dim newBook As Workbook
Set newBook = Workbooks.Add
'Rest of code to create query, run it, retrieve results, dump onto
'newBook, and populate into dProducts
newBook.Close SaveChanges:=False
End Sub
If I simply run Create_dProdsBySKU from within the Editor, the dictionary populates onto a new workbook, and closes. If I use the ProdLookup function within Excel, however, it never creates a new workbook - and if I put a watch on newBook, it shows it's got a value of ThisWorkbook.
Attempting to see the properties of newBook in the Watch window hangs Excel and I need to End Task from the Task Manager.
What am I missing?
If I use the ProdLookup function within Excel
If you are using the function as a UDF, it will not be permitted to create a new workbook. UDFs are limited to only returning a value to the cell containing the function call.

Can I get the text of the comments in the VBA code

Lets say I have the following:
Public Sub Information()
'TEST
End Sub
Is there a way to get "TEST" as a result?
Somehow through VBA?
E.g. - In PHP there is a good way to take the comments. Any ideas here?
Edit:
There should be a way, because tools like MZ-Tools are able to provide the comments when they generate the documentation.
You need to parse the code yourself, using the VBA Extensibility library (aka "VBIDE API"). Add a reference to the Microsoft Visual Basic for Applications Extentibility 5.3 type library, and then you can access types such as CodePane and VBComponent:
Sub FindComments()
Dim component As VBComponent
For Each component In Application.VBE.ActiveVBProject.VBComponents
Dim contents As String
contents = component.CodeModule.Lines(1, component.CodeModule.CountOfLines)
'"contents" now contains a string with the entire module's code.
Debug.Print ParseComments(contents) 'todo
Next
End Sub
Once you have a module's contents, you need to implement logic to find comments... and that can be tricky - here's some sample code to play with:
Sub Test()
Dim foo 'this is comment 1
'this _
is _
comment 2
Debug.Print "This 'is not a comment'!"
'..and here's comment 3
REM oh and guess what, a REM instruction is also a comment!
Debug.Print foo : REM can show up at the end of a line, given an instruction separator
End Sub
So you need to iterate the lines, track whether the comment is continuing on the next line / continued from the previous line, skip string literals, etc.
Have fun!
After some tests, I got to this solution:
simply pass the name of the code-module to the function and it will print all comment lines. Inline comments won't work(you have to change the condition)
Function findComments(moduleName As String)
Dim varLines() As String
Dim tmp As Variant
With ThisWorkbook.VBProject.VBComponents(moduleName).CodeModule
'split the lines of code into string array
varLines = Split(.lines(1, .CountOfLines), vbCrLf)
End With
'loop through lines in code
For Each tmp In varLines
'if line starts with '
If Trim(tmp) Like "'*" Then
'print comment line
Debug.Print Trim(tmp)
End If
Next tmp
End Function
You can use Microsoft Visual Basic for Applications Extensibility to examine code at runtime:
'Requires reference to Microsoft Visual Basic for Applications Extensibility
'and trusted access to VBA project object model.
Public Sub Information()
'TEST
End Sub
Public Sub Example()
Dim module As CodeModule
Set module = Application.VBE.ActiveVBProject.VBComponents(Me.CodeName).CodeModule
Dim code As String
code = module.lines(module.ProcStartLine("Information", vbext_pk_Proc), _
module.ProcCountLines("Information", vbext_pk_Proc))
Dim lines() As String
lines = Split(code, vbCrLf)
Dim line As Variant
For Each line In lines
If Left$(Trim$(line), 1) = "'" Then
Debug.Print "Found comment: " & line
End If
Next
End Sub
Note that the above example assumes that it's running in a Worksheet or Workbook code module (hence Me when locating the CodeModule). The best method for locating the correct module will depend on where you want to locate the procedure.
You could try with reading line by line of code in your module. Here is just idea returning first comment for further improvements:
Sub callIt()
Debug.Print GetComment("Module1")
End Sub
Function GetComment(moduleName As String)
Dim i As Integer
With ThisWorkbook.VBProject.VBComponents(moduleName).CodeModule
For i = 1 To .CountOfLines
If Left(Trim(.Lines(i, 1)), 1) = "'" Then
'here we have comments
'return the first one
GetComment = .Lines(i, 1)
Exit Function
End If
Next i
End With
End Function
Important! in Reference window add one to 'Microsoft Visual Basic for Applications Extensibility'.

Why does Excel VBA prompt me for a Macro name when I press Run Sub

I have the following code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim RR As Range
Dim TestArea As Range
Dim foremenList As Range
Dim workerList As Range
Dim workers As Range
Dim Foremen As Range
Dim i As Integer
Dim R As Range
Dim EmplList() As Variant
Set TestArea = Sheet90.Range("b4:q8", "b15:q19", "b26:q30")
Set foremenList = Sheet90.Range("V24:V30")
Set RR = Sheet90.Range("AA25:AA46")
i = 0
For Each R In RR.Cells
If Len(R.Value) > 0 Then
EmplList(i) = R.Value
i = i + 1
End If
Next R
Dim ValidStr As String
Set ValidStr = Join(EmplList, ",")
With Sheet90.Range("b26").Validation
.Delete
.Add xlValidateList, xlValidAlertStop, _
xlBetween, "1,2,3"
End With
Sheet90.Range("b40").Value = "Test"
End Sub
But when I press run to test it, it prompts me for a macro name.
Additionally, it does not trigger on Worksheet_Changeany more.
Is this an error (i.e. I forgot a semicolon or something) that consistently triggers Excel VBA to behave like this? If so, what should I look for in the future?
The reason you can't run this one with the Run Sub button is because it requires a parameter. If you want to run this standalone, one possibility is to run it in the Immediate Window so you can manually pass in the parameter. Since this one is expecting a more complex data type (range) you may want to create a small sub to call it so that you can properly create your range and pass that in. Then you can use the Run Sub on this sub which will call your other one.
As far is it not triggering on Worksheet_Change, I am not able to tell what is causing it just from what you posted. However, you do need to make sure that it is located on the code page for the worksheet you are trying to run it from. If you need the same one to run from multiple sheets, you should put it into a module and call it from each sheet's Worksheet_Change method.
You can't press F5 or the run button to run triggered code. You would have to make a change in the sheet where this code is located in order for the code to run. Also, if this code is not located in Sheet90, then you won't see anything happen because this code only makes changes to Sheet90. Lastly, to make sure events are enabled, you can run this bit of code:
Sub ReEnable_Events()
Application.EnableEvents = True
End Sub
Note that you will still have to enable macros.
The problem stems from two lines:
Set ValidStr = Join(EmplList, ",")
was not a valid use of the Set keyword (It's a string and not an object), and
Set TestArea = Sheet90.Range("b4:q8", "b15:q19", "b26:q30")
apparently has too many arguments.
According to Microsoft, it should be a single string argument like:
Set TestArea = Sheet90.Range("b4:q8, b15:q19, b26:q30")
Commenting both of these out made the code run fine both with the run sub button, and on the event.
The "Name Macro" dialog is some kind of error indicator, but I still don't know what it means, other than Code Borked

Methods in EXCEL Addin - XLL

How do I know which methods are available in my XLL module, in case i need to use / call any of them in my VBA code.
I can do this by calling the:
Application.Run()
method, in which I have to pass my macro-name as the parameter.
My question is about this macro-name: how do I know which macros are present in my XLL addin.
Any help is appreciated.
Cheers!!!!!!!!!!!
Tushar
You can use the Application.RegisteredFunctions method to give you a list of the functions in the XLLs that Excel has registered.
For example, the following code will list the XLL, the function name and the parameter types for the XLLs that are currently registered:
Public Sub ListRegisteredXLLFunctions()
Dim RegisteredFunctions As Variant
Dim i As Integer
RegisteredFunctions = Application.RegisteredFunctions
If IsNull(RegisteredFunctions) Then
Exit Sub
Else
Dim rng As Range
Set rng = Range("A1")
Set rng = rng.Resize(UBound(RegisteredFunctions, 1), UBound(RegisteredFunctions, 2))
rng.Value = RegisteredFunctions
End If
End Sub
Are you asking this from a code P.O.V? If you just want to check it out manually you can see that in the project explorer. Otherwise, I'd suggest just attempting to run the macro, but use an error handler in case the macro doesn't exist.
On Error GoTo badMacroCall
application.run(myMacro)
badMacroCall:
msgbox("That macro could not be run!")