How to refer to VBProject in a different xlsm Workbook? - vba

I need to write a macro that will create a new workbook test.xlsm and assign a vb code to it's first sheet (Sheet1).
Sub AddCode()
Dim wb As Workbook
Set wb = Workbooks.Add
Dim ws As Worksheet
Set ws = wb.Worksheets(1)
Dim code As String
code = "Sub test()" & vbCrLf & MsgBox "Test" & vbCrLf & "End Sub"
Dim lineCount As Integer
With wb.VBProject.VBComponents(ws.Name).CodeModule
lineCount = .CountOfLines
If lineCount > 0 Then
.DeleteLines 1, lineCount
End If
.AddFromString code
End With
wb.Save FileName:="C:\Users\Owner\Desktop\test.xlsm", FileFormat:=52
wb.Close
End Sub
I get subscript out of range error . I presume that I am referring to the wrong vb project
(PS: by "assigning a code to it's first sheet" I mean the following: Right Click on the Sheet1 -> View Code -> Paste my code to the Module )

You can save your workbook as an add-in and then load it into excel. Then you can add a reference to the add-in from the VBE editor and access the functions/subs.
You can google Chip Pearson's tutorials, and other such material for such stuff. Here is where he explains how you can manipulate the Visual Basic Editor to programmatically add code, etc. Possibly useful in your case.
Ref: http://www.cpearson.com/Excel/VBE.aspx
Just for ease of reference (all from the above website by Chip Pearson)
Adding A Module to a Project:
Sub AddModuleToProject()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents.Add(vbext_ct_StdModule)
VBComp.Name = "NewModule"
End Sub
and most importantly, adding a Subroutine to a Module:
Sub AddProcedureToModule()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim LineNum As Long
Const DQUOTE = """" ' one " character
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents("Module1")
Set CodeMod = VBComp.CodeModule
With CodeMod
LineNum = .CountOfLines + 1
.InsertLines LineNum, "Public Sub SayHello()"
LineNum = LineNum + 1
.InsertLines LineNum, " MsgBox " & DQUOTE & "Hello World" & DQUOTE
LineNum = LineNum + 1
.InsertLines LineNum, "End Sub"
End With
End Sub
Another way is simply to do the following
Dim FileNameStr as String, Path as String
' Set these two to the correct strings
Dim wb as Workbook
set wb = workbooks.open(Path & "\" & FileNameString)
Application.Run (wb.Name & "!NameOfYourMacro")
This way you can run a macro without arguments
Of course that's just the core of the code. You still need to clean up afterwards, close workbooks, maybe set them as readonly when opening, etc. but you get the drift.

Related

Export all charts in an Excel-workbook to a windows folder

I tried to run the following macro. Seems to work (I don`t have any error) but in the end only an empty folder opens (no picture exported). Please, help me with any advice! I am a beginner in VBA. Thank you very much!
Sub ExportAllCharts()
Dim objShell As Object
Dim objWindowsFolder As Object
Dim strWindowsFolder As String
Dim objSheet As Excel.Worksheet
Dim objChartObject As Excel.ChartObject
Dim objChart As Excel.Chart
'Select a Windows folder
Set objShell = CreateObject("Shell.Application")
Set objWindowsFolder = objShell.BrowseForFolder(0, "Select a Windows folder:", 0, "")
If Not objWindowsFolder Is Nothing Then
strWindowsFolder = objWindowsFolder.self.Path & "\"
For i = ThisWorkbook.Worksheets.Count To 1 Step -1
Set objSheet = ThisWorkbook.Worksheets(i)
If objSheet.ChartObjects.Count > 0 Then
For Each objChartObject In objSheet.ChartObjects
Set objChart = objChartObject.Chart
objChart.Export strWindowsFolder & objChart.Name & ".png"
Next
End If
Next
'Open the windows folder
Shell "Explorer.exe" & " " & strWindowsFolder, vbNormalFocus
End If
End Sub
Source code link
Now I`m trying to find a solution to export all the charts with the worksheet name + a suffix.
I wish I could insert the desired suffix (the same for all worksheets) into a pop-up window.
I have this code that renames all the worksheets, but I need to adapt it to rename them only partially. I thought maybe I could incorporate it into the initial macro.
Sub ChangeWorkSheetName()
Dim Rng As Range
Dim WorkRng As Range
On Error Resume Next
xTitleId = "Write the new Worksheets Name"
NewName = Application.InputBox("Name", xTitleId, "", Type:=2)
j = 1
For i = 1 To Application.Sheets.Count
If Application.Sheets(i).Visible Then
Application.Sheets(i).Name = NewName & j
j = j + 1
End If
Next
End Sub
Can anyone give me a suggestion? Thank you very much!
If you need to include charts on chart sheets you need a second loop:
Sub ExportAllCharts()
Dim objShell As Object
Dim objWindowsFolder As Object
Dim strWindowsFolder As String
Dim objSheet As Excel.Worksheet
Dim objChartObject As Excel.ChartObject
Dim objChart As Excel.Chart
'Select a Windows folder
Set objShell = CreateObject("Shell.Application")
Set objWindowsFolder = objShell.BrowseForFolder(0, "Select a Windows folder:", 0, "")
If Not objWindowsFolder Is Nothing Then
strWindowsFolder = objWindowsFolder.self.Path & "\"
'charts on chart sheets
For Each objChart In ThisWorkbook.Charts
objChart.Export strWindowsFolder & objChart.Name & ".png"
Next objChart
'chartobjects (on worksheets)
For Each objSheet In ThisWorkbook.Worksheets
For Each objChartObject In objSheet.ChartObjects
With objChartObject.Chart
.Export strWindowsFolder & .Name & ".png"
End With
Next
Next
'Open the windows folder
Shell "Explorer.exe" & " " & strWindowsFolder, vbNormalFocus
End If
End Sub

Mass importing modules & references in VBA

I have a set of ~10 modules/forms/classes that I like to use and reuse for many many projects. Usually these modules are coming in at the middle or the end of the project, not the start (So no making a template and using that from the start - I use that to make a few projects). Is there an easy way to mass import modules and references (that doesn't involve changing the file extension to .zip)?
I'm open to the file extension change method, but I'd like to exhaust my other resources first
You don't need any code or add-in for mass-importing. Simply save the commonly-used files in one specific folder that only contains these files.
It's a little-known VBE feature, that the Project Explorer supports drag-and-drop.
Then when you start a new VBA project, drag the files from the Windows Explorer and drop them onto the VBE's Project Explorer toolwindow - done.
The VBE's "import" file browse dialog is blocking multiple selections for some reason; Rubberduck fixes that by allowing multiple selections in its own "import" file browse dialog (off its Code Explorer toolwindow), and its "Export Active Project" tool makes it trivial to export an entire project's source code into a given folder like, say, a local git repository.
As for project references, no code can do that automatically without parsing some metadata that would have to be included somewhere in the module itself. Adding project references is annoying... Rubberduck's add/remove references dialog makes it much easier:
Disclaimer: I manage the Rubberduck open-source project.
You can do it from vba code ,
i get this code from here :
https://www.rondebruin.nl/win/s9/win002.htm
Public Sub ImportModules()
Dim wkbTarget As Excel.Workbook
Dim objFSO As Scripting.FileSystemObject
Dim objFile As Scripting.File
Dim szTargetWorkbook As String
Dim szImportPath As String
Dim szFileName As String
Dim cmpComponents As VBIDE.VBComponents
If ActiveWorkbook.Name = ThisWorkbook.Name Then
MsgBox "Select another destination workbook" & _
"Not possible to import in this workbook "
Exit Sub
End If
'Get the path to the folder with modules
If FolderWithVBAProjectFiles = "Error" Then
MsgBox "Import Folder not exist"
Exit Sub
End If
''' NOTE: This workbook must be open in Excel.
szTargetWorkbook = ActiveWorkbook.Name
Set wkbTarget = Application.Workbooks(szTargetWorkbook)
If wkbTarget.VBProject.Protection = 1 Then
MsgBox "The VBA in this workbook is protected," & _
"not possible to Import the code"
Exit Sub
End If
''' NOTE: Path where the code modules are located.
szImportPath = FolderWithVBAProjectFiles & "\"
Set objFSO = New Scripting.FileSystemObject
If objFSO.GetFolder(szImportPath).Files.Count = 0 Then
MsgBox "There are no files to import"
Exit Sub
End If
'Delete all modules/Userforms from the ActiveWorkbook
Call DeleteVBAModulesAndUserForms
Set cmpComponents = wkbTarget.VBProject.VBComponents
''' Import all the code modules in the specified path
''' to the ActiveWorkbook.
For Each objFile In objFSO.GetFolder(szImportPath).Files
If (objFSO.GetExtensionName(objFile.Name) = "cls") Or _
(objFSO.GetExtensionName(objFile.Name) = "frm") Or _
(objFSO.GetExtensionName(objFile.Name) = "bas") Then
cmpComponents.Import objFile.Path
End If
Next objFile
MsgBox "Import is ready"
End Sub
Public Sub ExportModules()
Dim bExport As Boolean
Dim wkbSource As Excel.Workbook
Dim szSourceWorkbook As String
Dim szExportPath As String
Dim szFileName As String
Dim cmpComponent As VBIDE.VBComponent
''' The code modules will be exported in a folder named.
''' VBAProjectFiles in the Documents folder.
''' The code below create this folder if it not exist
''' or delete all files in the folder if it exist.
If FolderWithVBAProjectFiles = "Error" Then
MsgBox "Export Folder not exist"
Exit Sub
End If
On Error Resume Next
Kill FolderWithVBAProjectFiles & "\*.*"
On Error GoTo 0
''' NOTE: This workbook must be open in Excel.
szSourceWorkbook = ActiveWorkbook.Name
Set wkbSource = Application.Workbooks(szSourceWorkbook)
If wkbSource.VBProject.Protection = 1 Then
MsgBox "The VBA in this workbook is protected," & _
"not possible to export the code"
Exit Sub
End If
szExportPath = FolderWithVBAProjectFiles & "\"
For Each cmpComponent In wkbSource.VBProject.VBComponents
bExport = True
szFileName = cmpComponent.Name
''' Concatenate the correct filename for export.
Select Case cmpComponent.Type
Case vbext_ct_ClassModule
szFileName = szFileName & ".cls"
Case vbext_ct_MSForm
szFileName = szFileName & ".frm"
Case vbext_ct_StdModule
szFileName = szFileName & ".bas"
Case vbext_ct_Document
''' This is a worksheet or workbook object.
''' Don't try to export.
bExport = False
End Select
If bExport Then
''' Export the component to a text file.
cmpComponent.Export szExportPath & szFileName
''' remove it from the project if you want
'''wkbSource.VBProject.VBComponents.Remove cmpComponent
End If
Next cmpComponent
MsgBox "Export is ready"
End Sub
Function FolderWithVBAProjectFiles() As String
Dim WshShell As Object
Dim FSO As Object
Dim SpecialPath As String
Set WshShell = CreateObject("WScript.Shell")
Set FSO = CreateObject("scripting.filesystemobject")
SpecialPath = WshShell.SpecialFolders("MyDocuments")
If Right(SpecialPath, 1) <> "\" Then
SpecialPath = SpecialPath & "\"
End If
If FSO.FolderExists(SpecialPath & "VBAProjectFiles") = False Then
On Error Resume Next
MkDir SpecialPath & "VBAProjectFiles"
On Error GoTo 0
End If
If FSO.FolderExists(SpecialPath & "VBAProjectFiles") = True Then
FolderWithVBAProjectFiles = SpecialPath & "VBAProjectFiles"
Else
FolderWithVBAProjectFiles = "Error"
End If
End Function
Function DeleteVBAModulesAndUserForms()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Set VBProj = ActiveWorkbook.VBProject
For Each VBComp In VBProj.VBComponents
If VBComp.Type = vbext_ct_Document Then
'Thisworkbook or worksheet module
'We do nothing
Else
VBProj.VBComponents.Remove VBComp
End If
Next VBComp
End Function

Add-in that adds code to new Excel Workbooks

I'm trying to change my add-in so that it, when you would open a new Workbook, will open a new Sheet and then create a new Event Procedure in that new Sheet.
I've gotten to the point where I can "Sheets.Add" and ".CreateEventProc" using a macro button combination, but the issue comes when trying to use the add-in's Workbook to automate the process. The add-in loads in first, thus "Set VBProj = ActiveWorkbook.VBProject" can't find the new active workbook.
Is it possible to do this? If so, is there a work around needed or am I just missing something obvious?
Here's what I have at the moment:
Option Explicit
Private WithEvents App As Excel.Application
Private Sub Workbook_Open()
Set App = Excel.Application
End Sub
Private Sub App_NewWorkbook(ByVal Wb As Workbook)
Range("T2").Value = 100
ActiveWorkbook.Sheets.Add
Call CreateEventProcedure
End Sub
Public Sub CreateEventProcedure()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim NumLines As Long
Dim LineNum As Long
Dim ProcName As String
Dim ProcKind As VBIDE.vbext_ProcKind
Const DQUOTE = """" ' one " character
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents("Sheet2")
Set CodeMod = VBComp.CodeModule
With CodeMod
LineNum = .CountOfDeclarationLines + 1
Do Until LineNum >= .CountOfLines Or ProcName = "Worksheet_Change"
ProcName = .ProcOfLine(LineNum, ProcKind)
LineNum = .ProcStartLine(ProcName, ProcKind) + _
.ProcCountLines(ProcName, ProcKind) + 1
Loop
If ProcName = "Worksheet_Change"
GoTo Exi
End If
'Now, create a Change Event on Sheet2
LineNum = .CreateEventProc("Change", "Worksheet")
For those who care, I got the process working with a minor complication. Here's what I did:
Option Explicit
Private WithEvents ExApp As Excel.Application
Public Sub ExApp_WorkbookOpen(ByVal Wb As Workbook)
Dim StrPrompt As String
Dim strTitle As String
Dim iRet As Integer
StrPrompt = "Want to create event?"
strTitle = "Event?"
On Error GoTo 0
iRet = MsgBox(StrPrompt, vbYesNo, strTitle)
If iRet = vbYes Then
ActiveWorkbook.Sheets.Add
Call CreateEventProcedure
End If
On Error GoTo 0
ActiveWorkbook.Sheets.Add
Call CreateEventProcedure
End If
End Sub
Private Sub Workbook_Open()
Set ExApp = Excel.Application
End Sub
I created the Yes/No prompt to Bypass the first 'run-time _Global' error and then the excel workbook actually loads, the prompt asks me again then I click yes and everything works fine. Also, this works with only one prompt needed when opening workbooks when already in excel.

How to automate find/replace code in VBA modules in MS Office 2013?

I have a large number of Excel Templates that contain VBA code that need to be updated. The Find method of the code-module object only returns true/false, not the location of the found string.
Is there any way to automate the find-and-replace procedure?
Add this code to a new macro-enabled workbook. Set the FIND_WHAT and REPLACE_WITH constants, open the other workbooks and run the code.
The original code comes from Charles Pearson's site
WARNING: Only basic testing has been done!
Option Explicit
Sub ReplaceTextInCodeModules()
' Must add a reference to "Microsoft Visual Basic For Applications Extensibility 5.3"
' Also must set "Trust access to the VBA project object model"
' See the url below for more info on these.
' Based on code found at:
' Source: www.cpearson.com/excel/vbe.aspx Copyright 2013, Charles H. Pearson
Dim theWorkbook As Workbook
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim numLines As Long ' end line
Dim lineNum As Long
Dim thisLine As String
Dim message As String
Dim numFound As Long
Const FIND_WHAT As String = "findthis"
Const REPLACE_WITH As String = "replaced"
numFound = 0
For Each theWorkbook In Application.Workbooks
If theWorkbook.Name <> ThisWorkbook.Name Then
If theWorkbook.HasVBProject Then
Set VBProj = theWorkbook.VBProject
For Each VBComp In VBProj.VBComponents
'Set VBComp = VBProj.VBComponents("Module1")
Set CodeMod = VBComp.CodeModule
With CodeMod
numLines = .CountOfLines
For lineNum = 1 To numLines
thisLine = .Lines(lineNum, 1)
If InStr(1, thisLine, FIND_WHAT, vbTextCompare) > 0 Then
message = message & theWorkbook.Name & " | " & VBComp.Name & " | Line #" & lineNum & vbNewLine
.ReplaceLine lineNum, Replace(thisLine, FIND_WHAT, REPLACE_WITH, , , vbTextCompare)
numFound = numFound + 1
End If
Next lineNum
End With
Next VBComp
End If
End If
Next theWorkbook
Debug.Print "Found: " & numFound
If message <> "" Then
Debug.Print message
End If
End Sub

Opening workbooks via hyperlink and then using Hyperlink name as workbook reference

I'm trying to take the hyperlink workbook name and put it into my code.
Sub Workbook()
Dim vbaname as string
Dim WBMaster As Workbook, WBSource As Workbook
Dim WSMaster As Worksheet, WSSource As Worksheet
Range("b7").Hyperlinks(1).Follow
'returns the hyperlink text "Vba Source test"
VbaName = """" & Range("B7").Text & """"
Set WBSource = Workbooks(VbaName)
I get a subscript out of range bug. Is there another way to do this. I just want to be able to put the hyperlink text into that bracket.
If you Debug.Print your VbaName it actually holds the value of B7 but the active window ( the followed one from hyperlink ). If you want to get the name of the workbook from the hyperlink, youre working in, then use this code
Sub GetWorkbookName()
MsgBox "the name of the workbook in the hyperlink is: " & vbCrLf & _
getWorkbookName(Range("B7").Text)
End Sub
Private Function getWorkbookName(hyperLink As String) As String
Dim i&
For i = 1 To Len(hyperLink)
If StrComp(Left(Right(hyperLink, i), 1), "\", vbTextCompare) = 0 Then
getWorkbookName = Right(hyperLink, i - 1)
Exit For
End If
Next i
End Function
On the other hand, I think you are trying to open the workbook from the hyperlink and assign a reference to it. The way you go about it it's not the right approach. I think you may want to consider doing it this way:
Sub Workbook()
Dim wbFromHyperLink As String
Dim WBSource As Workbook
MsgBox "the name of the workbook in the hyperlink is: " & vbCrLf & _
getWorkbookName(Range("B7").Text)
wbFromHyperLink = getWorkbookName(Range("B7").Text)
'Range("b7").Hyperlinks(1).Follow
Set WBSource = Workbooks.Open(Range("B7").Text)
' do not forget to close and free the object
' WBSource.Saved = True
' WBSource.Close
' Set WBSource = Nothing
End Sub
Private Function getWorkbookName(hyperLink As String) As String
Dim i&
For i = 1 To Len(hyperLink)
If StrComp(Left(Right(hyperLink, i), 1), "\", vbTextCompare) = 0 Then
getWorkbookName = Right(hyperLink, i - 1)
Exit For
End If
Next i
End Function