i have this excel macro in a file that calls a text file and converts it into excel then saves the new excel file to a specific location. each time I run the macro a new excel file will be created. however, I want this macro to add some functions inside this file in a specific sheet before saving it to the new directory and next time I open the new excel file the function should be running normally and if i go to the vba window i should see my function there.
1- i run the macro from my macro file
2- it opens my new excel and adds a function inside sheet1 and saves the excel file in it's new directory
any ideas on how to do that ?
thanks and best regards
Please, test the next code. It creates a new workbook (you may use the one you obtained opening the text file), inserts a standard module (named "TestModule"), creates a Sub ("WrittenFromAnotherWorbook") and then creates the same test Sub in the "Sheet1" module. The new saved workbook ("TestWorkbookWithCode.xlsm") can be found in the same path with the workbook keeping this code:
Sub CreateNewWorkbookAddMacro()
'It needs a reference to 'Microsoft Visual Basic for Applications Extensibility 5.3'
Dim wb As Workbook, project As VBProject, component As VBComponent
Dim code(1 To 3) As String, i As Long, boolFound As Boolean
Const moduleName As String = "TestModule"
Set wb = Workbooks.Add
Set project = wb.VBProject
code(1) = "Sub WrittenFromAnotherWorbook()"
code(2) = " MsgBox ""Hello from the new workbook!"""
code(3) = "End Sub"
For Each component In project.VBComponents
'if the module has already been created, exit the code:
If component.Name = moduleName Then boolFound = True: Exit For
Next
If Not boolFound Then
Set component = project.VBComponents.Add(vbext_ct_StdModule)
component.Name = moduleName
'Put the code in place:
For i = LBound(code) To UBound(code)
component.CodeModule.InsertLines i, code(i)
Next
End If
Set component = project.VBComponents(wb.Worksheets(1).CodeName)
'Put the (same) code in place:
For i = LBound(code) To UBound(code)
component.CodeModule.InsertLines i, code(i)
Next
wb.SaveAs ThisWorkbook.Path & "\TestWorkbookWithCode.xlsm", xlOpenXMLWorkbookMacroEnabled
End Sub
If you are not familiar with references adding, please use the next code to programmatically add the needed one:
Sub addExtenssibilityReference()
'Add a reference to 'Microsoft Visual Basic for Applications Extensibilty 5.3':
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:="{0002E157-0000-0000-C000-000000000046}", _
Major:=5, Minor:=3
End Sub
You must firstly run this code, save your workbook keeping the code and then run the one able to do what I explained above.
Please, test it and send some feedback.
Related
(Minimum requirements: Excel 2010 and Windows 7)
I have managed to use Bill Manville’s answer found in MSDN with minor changes. The suggested recursive code basically uses files’s Workbook_Open to create a separate instance and taht instance opens the file as editable with no prompts for read-only access.
Private Sub Workbook_Open()
Dim oXcl As Excel.Application
If Workbooks.Count > 1 Then
ThisWorkbook.Saved = True
ThisWorkbook.ChangeFileAccess xlReadOnly
Set oXcl = CreateObject("Excel.Application")
oXcl.ScreenUpdating = False
oXcl.Visible = True
oXcl.Workbooks.Open fileName:=ThisWorkbook.FullName, ReadOnly:=False
AppActivate oXcl.Caption
ThisWorkbook.Close SaveChanges:=False
Else
Call Continue_Open
End If
End Sub
The code works very well when Excel is already running as it creates a new instance of Excel and if a new Excel file is opened, it goes to a different Excel instance (running prior to it). But if the file with the Workbook_Open is the one that starts Excel, any further Excel files opened by double-clicking open within that Excel instance as it is the earliest run instance thus ceasing to be separate.
I have got as far as to be able to tell (Windows) whether that file starts Excel by using
Function NumberOfExcelInstances()
strComputer = "."
Set objWMI = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set proc = objWMI.ExecQuery("Select * from Win32_Process Where Name = 'Excel.exe'")
NumberOfExcelInstances = proc.Count
End Function
But I have not been able to find a way to tell NOT to use that Excel instance when opening new files. Any code should be bundled inside the Excel file with the Worbook_Open code.
How could I possibly include VBA code inside a file so that it opens in a separate Excel instance even when that file is the one that fires Excel?
After research on code at Application level, a working solution have been found. I am posting it, in case it is of interest to someone else.
When workbook opens the fist time it sets a workbook open event subroutine at Application level (rather than at Workbook level).
When a new workbook opens, the sub at Applictaion level opens a new instance with the workbook to be kept separate by recursivity - closes that workbook in the application instance that checks being separate thus removing the event handler from the application instance and sets that event handler and code on the newly created application instance.
All relevant code is included and it needs to be in three different modules.
1-a VBA Class Module named cXlEvents is created with the following code:
'VBA Class Module named cXlEvents
Public WithEvents appWithEvents As Application
'Instance variables
Dim sEventSetterPath As String
Dim sEventSetterName As String
Private Sub appWithEvents_WorkbookOpen(ByVal Wb As Workbook)
Call NewAppInstance(Wb, sEventSetterPath, sEventSetterName)
End Sub
2-ThisWorkbook Module includes:
'1-ThisWorkbook VBA Module calling events at
'Workbook level.
'2-At Workbook Open set Application level event
'handler and then instance code by calling subs
'held in VBA standard module.
Private Sub Workbook_Open()
Call SetEventHandler
Call NewAppInstance(Me)
End Sub
'Code to call "undo" special settings upon opening
'when file closes
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call UndoSettings
End Sub
3-All code necessary to create an instance at Workbook level Open event from a class which will end up at Application level is in a standard VBA Module:
'In a VBA standard Module
Dim oXlEvents As New cXlEvents
Sub SetEventHandler()
If oXlEvents.appWithEvents Is Nothing Then
Set oXlEvents.appWithEvents = Application
End If
End Sub
Sub NewAppInstance(wbWbook As Workbook, Optional sEventSetterPath As String, Optional sEventSetterName As String)
Dim oXcl As Excel.Application
Dim wbEventSet As Workbook
Dim lCaseNum As Long
Dim sResetMacro As String: sResetMacroName = "UndoSettings"
'Set instance variables
sEventSetterPath = ThisWorkbook.FullName
sEventSetterName = ThisWorkbook.Name
If wbWbook.ReadOnly And wbWbook.FullName = sEventSetterPath Then
MsgBox "Already open - please use open file.", , "WARNING"
wbWbook.Close False
Exit Sub
End If
If Workbooks.Count > 1 Then
If wbWbook.FullName <> sEventSetterPath Then
lCaseNum = 1
Set wbEventSet = Workbooks(1)
wbEventSet.Save
Application.Run "'" & sEventSetterName & "'!'" & sResetMacro & "'"
Else
lCaseNum = 2
Set wbEventSet = wbWbook
wbEventSet.Saved = True
End If
wbEventSet.ChangeFileAccess xlReadOnly
Set oXcl = CreateObject("Excel.Application")
oXcl.Workbooks.Open Filename:=sEventSetterPath, ReadOnly:=False
oXcl.Visible = True
Set oXlEvents.appWithEvents = Nothing
Select Case lCaseNum
Case Is = 1
AppActivate Application.Caption
Case Is = 2
AppActivate oXcl.Caption
End Select
wbEventSet.Close False
Else
Call Continue_Open
End If
End Sub
Sub Continue_Open()
'Code with special settings and procedures required for the workbook
End Sub
Sub UndoSettings()
'Code to "undo" any special settings when workbook opened
End Sub
I build a xlsm file that takes a database of a few hundred lines as input, processes them and outputs a few seperate .xlsm files, each containing some rearanged part of the input.
I would like to add a simple "Worksheet_BeforeDoubleClick" sub to each of the generated output files.
when working through the input, at the end of the for loop, I do the following:
Dim numLines As Integer
Set CodeCopy = ThisWorkbook.VBProject.VBComponents("Module2").CodeModule
numLines = CodeCopy.CountOfLines
NewBook.VBProject.VBComponents("Sheet1").CodeModule.AddFromString CodeCopy.Lines(1, numLines)
AccessMode:=xlExclusive, _
ConflictResolution:=Excel.XlSaveConflictResolution.xlLocalSessionChanges, _
FileFormat:=xlOpenXMLWorkbookMacroEnabled
NewBook.Saved = False
NewBook.SaveAs Filename:="cw_" & cw & "_" & myVendors(N), _
ConflictResolution:=Excel.XlSaveConflictResolution.xlLocalSessionChanges, _
FileFormat:=52
NewBook.Close
If I add a break before the "NewBook.Close", everything seems to work. The split file is shown as a seperate project and "Sheet2" contains the required code.
If I let the sub resume, the file is closed but if I reopen it manually the code is gone.
If I instead stop at the "NewBook.Close" and save the split file seperately the code remains in the file.
I have no idea why your code is gone after save - however, I generally think writing code using the VBE is not a good idea. And you need to allow this in the Trust Center Settings where it is usually disabled (for good reasons).
It is much easier to put the code into a template worksheet that sits in your workbook where your code sits. When create the new workbook, you simply copy this template sheet into it and voilà, all the code is copied with it. You can also put some formatting into the template - no need to do the formatting via code.
If you want, you can completely hide the template sheet - you just have to temporarily set it to visible before you copy it (use Application.ScreenUpdating = False to prevent flicker).
If you use the Copy-method of a worksheet without parameters, Excel will create a new Workbook with only this sheet. Of course, you can also create the new Workbook and use the Copy-method with Before or After-parameter.
Const TemplateSheetName = "Template"
With ThisWorkbook.Sheets(TemplateSheetName)
Application.ScreenUpdating = False
Dim saveVisibility As Long
saveVisibility = .Visible§
.Visible = xlSheetVisible
.Copy
.Visible = saveVisibility
Application.ScreenUpdating = True
End With
Dim newWb As Workbook, newWS As Worksheet
Set newWb = Workbooks(Workbooks.Count)
Set newWS = newWb.Sheets(TemplateSheetName)
newWS.Name = "MyNewDataSheet" ' You should assign a new name to the sheet
I have an excel workbook of xlsb format (let's call it "Child-workbook") automatically generated by another excel workbook ("Parent-workbook").
This so called "Child Workbook" has some sheets, containing graphs. I want these sheets to have a code that runs each time the user selects the sheet, and I want the "Parent Workbook" to transfer that code to "Child-workbook". The transfer has been done succesfully, the module does appear in the Child workbook's project until I close it (of course I save it before that...).
Here is an image of how it looks like after saving but before closing "Child.xlsb".
My problem is that when I reopen the Child workbook, the module is still there though, but its content is completely gone, it's empty.
Here is an image of how it looks like after saving, closing and reopening it.
Any advice?
Sub TransferModule()
Const MODULE_NAME As String = "DiagramMakro"
Dim MODULEFILE As String
MODULEFILE = "C:\Modul.txt"
Workbooks("Parent.xlsm").VBProject.VBComponents(MODULE_NAME).Export MODULEFILE
For i = 1 To Workbooks("CHILD.xlsb").Sheets.Count
If Workbooks("CHILD.xlsb").Worksheets(i).Name Like "Diagram*" Then
Workbooks("CHILD.xlsb").Worksheets(i).Activate
Workbooks("CHILD.xlsb").VBProject.VBComponents(ActiveSheet.CodeName).Name = MODULE_NAME
Workbooks("CHILD.xlsb").VBProject.VBComponents.Item("CHILD.xlsb").CodeModule.AddFromFile (MODULEFILE)
End If
Next i
Kill MODULEFILE
End Sub
And here is the Module to transfer:
Private Sub Worksheet_Activate()
Dim myChart As Chart
Dim mySeries As SeriesCollection
Set myChart = ActiveSheet.ChartObjects(1).Chart
Set mySeries = myChart.SeriesCollection
For i = 3 To mySeries.Count
mySeries.Item(i).Format.Line.Visible = msoFalse
Next i
End Sub
Maybe problems with txt file
Try read this first and run CreateEventProcedure
VBA Editor Constants
I am opening and converting a .csv to an .xlsx file on a shared drive location. Once the .xlsx is created it remains open. The macros to do this are in a master Workbook located on the shared drive as well. Right now I have it set for a user to open the workbook and select a command button that opens and converts the file.
The user the selects the template needed from a list box and clicks a second command button which runs the following macro that calls a specific macro which converts the .xlsx just created to the correct format.
All my macros work except the macro below. It stops at the first Workbooks.. Right now the newly created .xlsx is the only .xlsx in its folder but eventually there may be multiple .xlsx files created from multiple .csv files.
Sub Run_Macros()
'IF USER SELECTED FXX_Rejects
If Range("A8").Value = Range("A2").Value Then
'ACTIVATE OTHER WORKBOOK
Workbooks("*.xlsx").Activate
Workbooks("*.xlsx").Sheets("Sheet1").Select
'AND THEN RUN APPROPRIATE MACRO
Call aaLayout
'IF USER SELECTED LXX_Rejects
ElseIf Range("A8").Value = Range("A3").Value Then
'ACTIVATE OTHER WORKBOOK
Workbooks("*.xlsx").Activate
Workbooks("*.xlsx").Sheets("Sheet1").Activate
'AND THEN RUN APPROPRIATE MACRO
Call abLayout
'IF USER SELECTED HXXXX_Rejects
ElseIf Range("A8").Value = Range("A4").Value Then
'ACTIVATE OTHER WORKBOOK
Workbooks("*.xlsx").Activate
Workbooks("*.xlsx").Sheets("Sheet1").Activate
'AND THEN RUN APPROPRIATE MACRO
Call acLayout
'IF USER SELECTED SXXX_Rejects
ElseIf Range("A8").Value = Range("A5").Value Then
'ACTIVATE OTHER WORKBOOK
Workbooks("*.xlsx").Activate
Workbooks("*.xlsx").Sheets("Sheet1").Activate
'AND THEN RUN APPROPRIATE MACRO
Call adLayout
End If
End Sub
I have edited my question post and added the macro below that locates and converts the .CSV files preparing them for templating. Maybe this can assist answering my original question.
Sub CSVFiles()
Dim MyFiles As String, ThisMonth As String
Dim startPath As String
Dim wb As Workbook
ThisMonth = Format(Date, "mmmm")
startPath = "\\XXX\2017\" & ThisMonth & "\"
MyFiles = Dir(startPath & "*.csv")
Do While MyFiles <> ""
Set wb = Workbooks.Open(startPath & MyFiles)
Call XLSXConvert
'Converts all csv files, saves and closes files. Prepares files for full template creation.
wb.SaveAs fileName:=startPath & Replace (MyFiles, ".csv", ".xlsx"),FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
wb.Close
MyFiles = Dir
Loop
End Sub
Your problem is with "*.xlsx". Operating system commands accept the * as a wildcard but you're not calling an operating system command. You are calling a VBA method on the Workbooks collection of objects that uses a number, usually the order in which the files were opened, as its identifier. Workbooks().Activate does not go out to the folder and look at all the available files and pick one that matches. Workbooks.Open() would do that but it still doesn't accept wildcards, and you said the workbook you want is already open.
Workbooks().Activate looks through the list of open workbooks for an identifier. The object you're trying to Activate was created in the code that ran before you get to this point, that we can't see from your posting. Somewhere in that code, you could assign that object to a variable and then you could create an argument in this code so you could pass in that variable.
What you could also do, IF you know that the only .xlsx file you have open is the one you want, is loop through all the open files and check the name to see if it ends in .xlsx and if it does assign that to a variable. Illustrated below:
Sub Run_Macros()
Dim wb As Workbook
Dim target As Workbook
Dim wbs As Workbooks
Set wbs = Workbooks
For Each wb In wbs
If LCase(Right(wb.FullName, 5)) = ".xlsx" Then
Set target = wb
End If
Next wb
'IF USER SELECTED FXX_Rejects
If Range("A8").Value = Range("A2").Value Then
'ACTIVATE OTHER WORKBOOK
target.Activate
target.Sheets("Sheet1").Select
'AND THEN RUN APPROPRIATE MACRO
Call aaLayout
'IF USER SELECTED LXX_Rejects
ElseIf Range("A8").Value = Range("A3").Value Then
'ACTIVATE OTHER WORKBOOK
target.Activate
target.Sheets("Sheet1").Activate
'AND THEN RUN APPROPRIATE MACRO
Call abLayout
'IF USER SELECTED HXXXX_Rejects
ElseIf Range("A8").Value = Range("A4").Value Then
'ACTIVATE OTHER WORKBOOK
target.Activate
target.Sheets("Sheet1").Activate
'AND THEN RUN APPROPRIATE MACRO
Call acLayout
'IF USER SELECTED SXXX_Rejects
ElseIf Range("A8").Value = Range("A5").Value Then
'ACTIVATE OTHER WORKBOOK
target.Activate
target.Sheets("Sheet1").Activate
'AND THEN RUN APPROPRIATE MACRO
Call adLayout
End If
End Sub
I have created a workbook which is named "AirHours" & the date from another workbook. I want to use this workbook throughout the entire project. The code below works, but I keep getting a run-time error 9 "Subscript out of range".
I'm trying to create a workbook for my report and have my data remain in its original form. The new workbook will be used in a number of modules. The runtime error seems to occur at SET WSD3=Workbook.
Maybe I going the wrong way on creating a public workbook. Thanks for your help.
Option Explicit
Public WSD3 As Workbook
Public Sub addNewWorkBook()
Dim NewName As String
Application.DisplayAlerts = False
NewName = "AirHours" & Workbooks("AirTimeWorkBookBeta").Worksheets("Data").Cells(2, 1).Value
Workbooks.Add
ActiveWorkbook.SaveAs NewName
Set WSD3 = Workbooks("NewName")
End Sub
Set WSD3 to your new workbook when you open it
Option Explicit
Public WSD3 As Workbook
Public Sub addNewWorkBook()
Dim NewName As String
Application.DisplayAlerts = False
NewName = "AirHours" & Workbooks("AirTimeWorkBookBeta").Worksheets("Data").Cells(2, 1).Value
Set WSD3 = Workbooks.Add
WSD3.SaveAs NewName
End Sub
Along with scott's suggestion, part of the problem is that when referencing workbooks with Workbooks("workbookname.ext") you need to provide the extension, like .xls or .xlsm, so you'll need to update the where you're referencing Workbooks("AirTimeWorkBookBeta") to include that workbook's appropriate extension.
You could also get the error if that workbook isn't currently open when the macro is run.
Additionally, make sure that workbook contains a sheet named Data