I am trying to update the source to all the links in a word report by using a macro in word VBA. I want to be able to offer a dialog box to user then they select file and it replaces current source in all links in the word doc. The code i have below works but really slowly. I also seem to have to open excel in the background or the links wont work? not sure why this is??
It seems to go through eack link in tuen. Is there a way to globally change all the links at the same time possibly using find and repalce? please any help is greatly appreciated! I need this for a reprot in work and so i need to find a solution as soon as possible.
Private Sub CommandButton1_Click()
Dim OldFile As String
Dim xlsobj As Object
Dim xlsfile_chart As Object
Dim dlgSelectFile As FileDialog 'FileDialog object '
Dim thisField As Field
Dim selectedFile As Variant
'must be Variant to contain filepath of selected item
Dim newFile As Variant
Dim fieldCount As Integer '
Dim x As Long
On Error GoTo LinkError
'create FileDialog object as File Picker dialog box
Set dlgSelectFile = Application.FileDialog
(FileDialogType:=msoFileDialogFilePicker)
With dlgSelectFile
.Filters.Clear 'clear filters
.Filters.Add "Microsoft Excel Files", "*.xls, *.xlsb, *.xlsm,
*.xlsx" 'filter for only Excel files
'use Show method to display File Picker dialog box and return user's
action
If .Show = -1 Then
'step through each string in the FileDialogSelectedItems collection
For Each selectedFile In .SelectedItems
newFile = selectedFile 'gets new filepath
Next selectedFile
Else 'user clicked cancel
Exit Sub
End If
End With
Set dlgSelectFile = Nothing
' update fields
Set xlsobj = CreateObject("Excel.Application")
xlsobj.Application.Visible = False
Set xlsfile_chart = xlsobj.Application.Workbooks.Open(newFile,
ReadOnly = True)
Application.ScreenUpdating = False
With xlsobj.Application
.calculation = xlcalculationmanual
.enableevents = False
End With
fieldCount = ActiveDocument.Fields.Count
For x = 1 To fieldCount
With ActiveDocument.Fields(x)
If .Type = 56 Then
.LinkFormat.SourceFullName = newFile
End If
End With
Next x
With xlsobj.Application
.calculation = xlcalculationmanual
.enableevents = True
End With
Application.ScreenUpdating = True
MsgBox "Data has been sucessfully linked to report"
'clean up
xlsfile_chart.Close SaveChanges:=False
Set xlsfile_chart = Nothing
xlsobj.Quit
Set xlsobj = Nothing
Exit Sub
LinkError:
Select Case Err.Number
Case 5391 'could not find associated Range Name
MsgBox "Could not find the associated Excel Range Name " & _
"for one or more links in this document. " & _
"Please be sure that you have selected a valid " & _
"Quote Submission input file.", vbCritical
Case Else
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical
End Select
' clean up
Set xlsfile_chart = Nothing
xlsobj.Quit
Set xlsobj = Nothing
End Sub
Dim FolderName As String
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
.Show
On Error Resume Next
FolderName = .SelectedItems(1)
On error go to 0
End With
If FolderName = "" Then
Exit Sub
End If
'Continue with code using FolderName as your source path
Hopefully this will serve as a good starting point for you. This will get you the path of the source folder and store it in FolderName. You can then build your link using:
CompletePath = FolderName + [FileNameGoesHere]
(Don't forget to make sure your FolderName has a "\" on the end, else the path will be incorrectly formatted, if it doesn't you can add it in or perform a check to ensure it is present on the end of the FolderName string
Related
I have a code that I found to loop through all of the files in the folder named Loop_AllWordFiles_inFolder and it calls whatever code you put in to execute some kind of action on the word documents in your selected folder. This code will run.
However I run into a problem when I try to have it call upon the code.. I don't know how to make them run together. The code it's calling is called ExtractSubject which is the action I need executed. I found this code online which runs through one file at a time and I'm trying to combine it with the looping files.
I'm new to VBA and I'm not sure how to fix the ExtractSubject code so they can run together. My end goal is to have two columns one with the title of the file and then beside it in the next cell the subject which I will be extracting. Something like this 1
Also I can't open a file without this read-only pop-up2 so if anyone knows how to fix that it would be appreciated but this is not my main concern atm.
Here's the two codes:
Option Explicit
Dim wb As Workbook
Dim path As String
Dim myFile As String
Dim myExtension As String
Dim myFolder As FileDialog
Dim wdApp As Object, wddoc As Object
Sub Loop_AllWordFiles_inFolder()
Set wdApp = CreateObject("Word.Application")
'Optimize Macro Speed
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'Retrieve Target Folder Path From User
Set myFolder = Application.FileDialog(msoFileDialogFolderPicker)
With myFolder
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show <> -1 Then GoTo NextCode
path = .SelectedItems(1) & "\"
End With
' if the User select "Cancel"
NextCode:
path = path
If path = "" Then GoTo ResetSettings
' Target File Extension
myExtension = "*.doc"
' Target Path with Ending Extention
myFile = Dir(path & myExtension)
' Loop through all doc files in folder
Do While myFile <> ""
Set wddoc = wdApp.Documents.Open(fileName:=path & myFile)
' HERE you call your other routine
Call ExtractSubject
wddoc.Close SaveChanges:=False
myFile = Dir
Loop
Application.DisplayAlerts = PrevDispAlerts
MsgBox "Finished scanning all files in Folder " & path
ResetSettings:
' Reset Macro Optimization Settings
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Set wdApp = Nothing
End Sub
Sub ExtractSubject()
Dim cDoc As Word.Document
Dim cRng As Word.Range
Dim i As Long
i = 2
Dim wordapp As Object
Set wordapp = CreateObject("word.Application")
wordapp.Documents.Open "c:\code practice\file1"
wdApp.Visible = True
Set wddoc = ActiveDocument
Set cRng = wddoc.Content
With cRng.Find
.Forward = True
.Text = "SUBJECT:"
.Wrap = wdFindStop
.Execute
'Collapses a range or selection to the starting or ending position
cRng.Collapse Word.WdCollapseDirection.wdCollapseEnd
cRng.MoveEndUntil Cset:="JOB"
Cells(i, 1) = cRng
cRng.Collapse Word.WdCollapseDirection.wdCollapseEnd
.Execute
i = i + 1
End With
wordapp.Quit
Set wordapp = Nothing
End Sub
I think something like this should be close to what you're trying to do. Note you don't want all your variables as Globals - anything which needs to be shared between methods can be passed as an argument or returned as a function result.
Sub Loop_AllWordFiles_inFolder()
Const FILE_EXT As String = ".doc"
Dim wb As Workbook
Dim path As String
Dim myFile As String, theSubject As String
Dim wdApp As Object, wdDoc As Object
'Retrieve Target Folder Path From User
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Select A Target Folder"
.AllowMultiSelect = False
If .Show = -1 Then path = .SelectedItems(1) & "\"
End With
If Len(path) = 0 Then Exit Sub
'path = "C:\Temp\Test\" 'testing only
myFile = Dir(path & "*" & FILE_EXT) ' Target Path with Ending Extention
If Len(myFile) = 0 Then
MsgBox "No Word files found"
Exit Sub
End If
Set wdApp = CreateObject("Word.Application")
wdApp.Visible = True
Optimize '(don't really need this for this code though...)
Do While myFile <> ""
Set wdDoc = wdApp.Documents.Open(Filename:=path & myFile)
theSubject = ExtractSubject(wdDoc) 'extract subject from wdDoc
wdDoc.Close SaveChanges:=False
If Len(theSubject) > 0 Then 'subject was found?
Name path & myFile As path & theSubject & FILE_EXT 'rename the file
Else
'output any problems
Debug.Print "Subject not found in '" & path & myFile & "'"
End If
myFile = Dir 'next file
Loop
wdApp.Quit 'no need to set to Nothing
Optimize False 'turn off speed enhancements
'Application.DisplayAlerts = PrevDispAlerts '?????
MsgBox "Finished scanning all files in Folder " & path
End Sub
'Return text between "SUBJECT:" and "JOB" in word document `wdDoc`
Function ExtractSubject(wdDoc As Word.document) As String
Dim cRng As Word.Range
Set cRng = wdDoc.content
With cRng.Find
.Forward = True
.Text = "SUBJECT:"
.Wrap = wdFindStop
If .Execute() Then
cRng.Collapse Word.WdCollapseDirection.wdCollapseEnd
cRng.MoveEndUntil Cset:="JOB"
ExtractSubject = Trim(cRng.Text)
End If
End With
End Function
'make changes to application settings to optimize macro speed in excel
Sub Optimize(Optional goFast As Boolean = True)
With Application
.ScreenUpdating = Not goFast
.EnableEvents = Not goFast
.Calculation = IIf(goFast, xlCalculationManual, xlCalculationAutomatic)
End With
End Sub
Try taking a look at this and see if it helps. From what I understand you are just trying to call one function in the middle of another.
From the first sentence of that link: "To call a Sub procedure from another procedure, type the name of the procedure and include values for any required arguments."
I have about a dozen linked excel OLE objects in my word document. These files get moved around the network frequently so I need an easy intuitive way for the underlying links to be updated. I tried a code I found on this website however while it updated the source it seems to also update what is being shown in the object itself so all the objects change to whatever worksheet was in focus when the excel workbook was saved last. I'm trying to retain format and range while updating the source. Any help would be great.
Here is the code I tried presently:
Private Sub CommandButton1_Click()
Dim OldFile As String
Dim xlsobj As Object
Dim xlsfile_chart As Object
Dim dlgSelectFile As FileDialog 'FileDialog object '
Dim thisField As Field
Dim selectedFile As Variant
'must be Variant to contain filepath of selected item
Dim newFile As Variant
Dim fieldCount As Integer '
Dim x As Long
On Error GoTo LinkError
'create FileDialog object as File Picker dialog box
Set dlgSelectFile = Application.FileDialog
(FileDialogType:=msoFileDialogFilePicker)
With dlgSelectFile
.Filters.Clear 'clear filters
.Filters.Add "Microsoft Excel Files", "*.xls, *.xlsb, *.xlsm,*.xlsx" 'filter
for only Excel files
'use Show method to display File Picker dialog box and return user's action
If .Show = -1 Then
'step through each string in the FileDialogSelectedItems collection
For Each selectedFile In .SelectedItems
newFile = selectedFile 'gets new filepath
Next selectedFile
Else 'user clicked cancel
Exit Sub
End If
End With
Set dlgSelectFile = Nothing
'update fields
Set xlsobj = CreateObject("Excel.Application")
xlsobj.Application.Visible = False
Set xlsfile_chart = xlsobj.Application.Workbooks.Open(newFile, ReadOnly =
True)
Application.ScreenUpdating = False
With xlsobj.Application
.calculation = xlcalculationmanual
.enableevents = False
End With
fieldCount = ActiveDocument.Fields.Count
For x = 1 To fieldCount
With ActiveDocument.Fields(x)
If .Type = 56 Then
.LinkFormat.SourceFullName = newFile
End If
End With
Next x
With xlsobj.Application
.calculation = xlcalculationmanual
.enableevents = True
End With
Application.ScreenUpdating = True
MsgBox "Data has been sucessfully linked to report"
'clean up
xlsfile_chart.Close SaveChanges:=False
Set xlsfile_chart = Nothing
xlsobj.Quit
Set xlsobj = Nothing
Exit Sub
LinkError:
Select Case Err.Number
Case 5391 'could not find associated Range Name
MsgBox "Could not find the associated Excel Range Name " & _
"for one or more links in this document. " & _
"Please be sure that you have selected a valid " & _
"Quote Submission input file.", vbCritical
Case Else
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical
End Select
' clean up
Set xlsfile_chart = Nothing
xlsobj.Quit
Set xlsobj = Nothing
End Sub
I have code that auto changes formula links from another workbook.
On my laptop (Windows 10 office 365) I get a runtime error and asks me to debug the following line.
ThisWorkbook.ChangeLink Name:=strLink, NewName:=strLinkNew, Type:=xlExcelLinks
It runs on a computer running windows 7 Office 2010.
The whole code:
Dim strFile As String
Dim aLinks As Variant
Dim i As Long
Dim strLink As String
Dim strLinkNew As String
With Application.FileDialog(msoFileDialogFilePicker)
.AllowMultiSelect = False
.Show
If .SelectedItems.Count > 0 Then
strLinkNew = .SelectedItems(1)
aLinks = ThisWorkbook.LinkSources(xlExcelLinks)
If Not IsEmpty(aLinks) Then
For i = 1 To UBound(aLinks)
strLink = aLinks(i)
If strLink Like "*\CRiSP*.xlsm" Then
'Change Linked File
Sheets("Links").Select
ThisWorkbook.Worksheets("Links").Unprotect "MYPASSWORD"
ThisWorkbook.ChangeLink Name:=strLink, NewName:=strLinkNew, Type:=xlExcelLinks
ThisWorkbook.Worksheets("Links").Protect "MYPASSWORD"
End If
Next
End If
End If
End With
Sheets("Main Menu").Select
Cells(1, 1).Select
Dim flToSave As Variant
Dim flName As String
Dim flFormat As Long
flFormat = ActiveWorkbook.FileFormat
flName = Range("A1") & Range("A2").Text
flToSave = Application.GetSaveAsFilename _
(ThisWorkbook.Path & "\" & flName, filefilter:="Excel Files (*.xlsm), *.xlsm", _
Title:="Save FileAs...")
If flToSave = False Then
Exit Sub
Else
ThisWorkbook.SaveAs Filename:=flToSave, FileFormat:=flFormat
End If
End Sub
I had the same error 1004 challenge but solved it by ensuring that both the 'Name' and 'NewName' files were present at their paths.
this function will update the links while at the same time fixes a strange bug i was trying to stomp on for a while, if a link is not used in the active sheet then excel gives you error 1004
'''''''''''''''''
Private Function UpdateXlsLinkSource(oldLinkPathAndFile As String, newLinkPathAndFile As String) As Boolean
UpdateXlsLinkSource = False
Dim lSources As Variant
lSources = ThisWorkbook.LinkSources(xlExcelLinks) 'array that contains all the links with path to excel files
Dim FILE_NAME As String
FILE_NAME = Right(newLinkPathAndFile, Len(newLinkPathAndFile) - InStrRev(newLinkPathAndFile, "\")) 'name of the file without path
Dim theFileIsAlreadyOpen As Boolean
theFileIsAlreadyOpen = file_open_module.IsWorkBookOpen(FILE_NAME) 'will check if the file is is open and return true or false
'check if a file with the same name is already open
If theFileIsAlreadyOpen Then
newLinkPathAndFile = Workbooks(FILE_NAME).PATH & "\" & Workbooks(FILE_NAME).Name 'use the open file
Else
Workbooks.Open FileName:=newLinkPathAndFile 'open the file if it wasn't already open
End If
theFileIsAlreadyOpen = True
'CHECK IF THE FILE NEEDS UPDATING
If newLinkPathAndFile = oldLinkPathAndFile Then
UpdateXlsLinkSource = True 'if the link is unchanged update the values
Exit Function
Else
'step thru the existing links and see if it exists
For Each Link In lSources
If Link = oldLinkPathAndFile Then
'''''''''''''''''''''''''''''''''''''
For Each SHEET In ThisWorkbook.Worksheets 'this seemingly useless loop handles a bug where if a link is not referenced in the active sheet it crashes
SHEET.Activate
On Error Resume Next
'''''''''''''''''''''''''''''''''''''
ThisWorkbook.Activate
ThisWorkbook.ChangeLink Name:=Link, NewName:=newLinkPathAndFile, Type:=xlExcelLinks 'update the link
UpdateXlsLinkSource = True
'''''''''''''''''''''''''''''''''''''
If Err = 0 Then
On Error GoTo 0
Exit For
End If
Next SHEET
'''''''''''''''''''''''''''''''''''''
Exit For
End If
Next Link
'check if the link was found AND WARN IF IT WAS NOT
If Not UpdateXlsLinkSource Then
MsgBox "Link to target not found"
Exit Function
End If
If Not theFileIsAlreadyOpen Then 'CHECK IF THE FILE IS CLOSED, IF IT IS THEN OPEN IT
Workbooks.Open (newLinkPathAndFile)
End If
End If
End Function
'''''''''''''
I'm trying to open an Excel file from Access and do some stuff with It, but code is not stable. Sometimes It works, other times not. Here's how I do this:
Dim FilePath As String
Dim ExcelApp As Excel.Application
FilePath = "C:\Users\Lucky\Desktop\Test.xls"
Set ExcelApp = CreateObject("Excel.Application")
ExcelApp.Workbooks.Open (FilePath)
With ExcelApp
'do some stuff here
End With
ExcelApp.Workbooks.Close
Set ExcelApp = Nothing
I've also noticed that once I run code, Excel starts proccess under Task Manager, that has to be killed manually in order to get code working again. Otherwise I get two types of error with Excel file:
one is that If I click Excel file, It doesn't open, It just flashes for a second and dissapears
and other is that Excel file opens in "read-only" mode...
So I reckon there is some flaw when file is closed in my code. How can I fix this ?
I can't see what's wrong with your code - maybe the path to the desktop?
This is the code I usually use - I've added another function to help choose the file. It uses late binding, so no need to set a reference to Excel - you don't get the IntelliSense and can't use Excel constants such as xlUp - you have to use the numerical equivalent.
Public Sub Test()
Dim oXLApp As Object
Dim oXLWrkBk As Object
Dim oXLWrkSht As Object
Dim vFile As Variant
Dim lLastRow As Long
vFile = GetFile()
Set oXLApp = CreateXL
Set oXLWrkBk = oXLApp.WorkBooks.Open(vFile, False)
Set oXLWrkSht = oXLWrkBk.WorkSheets(1) 'First sheet. Can also use "Sheet1", etc...
lLastRow = oXLWrkSht.Cells(oXLWrkSht.Rows.Count, "A").End(-4162).Row '-4162 = xlUp
MsgBox "Last row in column A is " & lLastRow
oXLWrkBk.Close False
oXLApp.Quit
Set oXLWrkBk = Nothing
Set oXLApp = Nothing
End Sub
Public Function CreateXL(Optional bVisible As Boolean = True) As Object
Dim oTmpXL As Object
'''''''''''''''''''''''''''''''''''''''''''''''''''''
'Defer error trapping in case Excel is not running. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
Set oTmpXL = GetObject(, "Excel.Application")
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
'If an error occurs then create an instance of Excel. '
'Reinstate error handling. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
If Err.Number <> 0 Then
Err.Clear
On Error GoTo ERROR_HANDLER
Set oTmpXL = CreateObject("Excel.Application")
End If
oTmpXL.Visible = bVisible
Set CreateXL = oTmpXL
On Error GoTo 0
Exit Function
ERROR_HANDLER:
Select Case Err.Number
Case Else
MsgBox "Error " & Err.Number & vbCr & _
" (" & Err.Description & ") in procedure CreateXL."
Err.Clear
End Select
End Function
Function GetFile(Optional startFolder As Variant = -1, Optional sFilterName As String = "") As Variant
Dim fle As Object
Dim vItem As Variant
'''''''''''''''''''''''''''''''''''''''''''
'Clear the file filter and add a new one. '
'''''''''''''''''''''''''''''''''''''''''''
Application.FileDialog(3).Filters.Clear
Application.FileDialog(3).Filters.Add "'Some File Description' Excel Files", "*.xls, *.xlsx, *.xlsm"
Set fle = Application.FileDialog(3)
With fle
.Title = "Select a file"
.AllowMultiSelect = False
If startFolder = -1 Then
.InitialFileName = CurrentProject.Path
Else
If Right(startFolder, 1) <> "\" Then
.InitialFileName = startFolder & "\"
Else
.InitialFileName = startFolder
End If
End If
If .Show <> -1 Then GoTo NextCode
vItem = .SelectedItems(1)
End With
NextCode:
GetFile = vItem
Set fle = Nothing
End Function
I have managed to solve my problem. There is nothing wrong with code in my question, except that instead of declaring
Dim ExcelApp As Excel.Application
It's better to use
Dim ExcelApp As Object
But much bigger problem is with code that does changes in Excel, such as this line:
x = Range(Cells(1, i), Cells(Rows.Count, i).End(xlUp)).Value
And correct synthax is:
x = ExcelApp.Range(ExcelApp.Cells(1, i), ExcelApp.Cells(ExcelApp.Rows.Count, i).End(xlUp)).Value 'maybe also better to replace xlUp with -4162
So, whenever you use some code for Excel file from Access, DON'T FORGET to reference everything to Excel object. And ofcourse, before everything, a proper reference must be set in VBA console, in my case Microsoft Office 15.0 library.
I have +500 Excel files (*.xls) having macros, all located in same folder.
I want to remove all macros from these files. Removing macros manually one by one from all files will take too much time.
Is it possible to create a new macro in a separate excel file which will remove all macros from these closed files?
Thanks for your guidance in advance.
I have coded routines around macro ListComponentsSingleWbk to meet your requirement. I have tested then with a variety of workbooks and I believe they provide the functionality you seek.
Both ListComponentsCtrl and DeleteLinesCtrl contain the statement Path = .... You will need to amend these statements to match the path of your folder.
I use macro ListComponentsSingleWbk to provide daily backups of the macros I am developing. I have coded ListComponentsCtrl to call ListComponentsSingleWbk for every XLS files in a folder.
I suggest you run ListComponentsCtrl before you do anything else. It will create a file with the name “BkUp yymmdd hhmm.txt” where “yymmdd hhmm” represent the current date and time. Following the run, “BkUp yymmdd hhmm.txt” will contain:
The name of every workbook it has found.
The name of every component within a workbook that might contain code.
If there is code within a component, a list of that code.
Running ListComponentsCtrl will ensure you have a complete backup if you discover in a month’s time that you have deleted macros from the wrong workbooks.
DeleteCodeCtrl calls DeleteCodeSingleWbk for every XLS files in a folder.
DeleteCodeSingleWbk:
Removes all standard and class modules from a workbook.
Clears any code from the code modules of the worksheets.
Clears any code from the code module of ThisWorkbook.
Option Explicit
' This module was built from information scattered across many sites. The
' most useful were:
' http://vbadud.blogspot.co.uk/2007/05/insert-procedure-to-module-using.html
' http://support.microsoft.com/kb/282830
' http://msdn.microsoft.com/en-us/library/aa443716(v=vs.60).aspx
' http://www.ozgrid.com/forum/showthread.php?t=32709
' This module needs a reference to:
' "Microsoft Visual Basic for Applications Extensibility n.n"
' The security system will probably prevent access to VBComponents unless you:
' For Excel 2003, from Excel (not VB Editor)
' Click Tools
' Click Macro
' Click Security
' Click Trusted Publishers
' Tick Trust access to Visual Basic Project
' For other versions of Excel search for "programmatic access to Visual Basic project not trusted"
Sub DeleteCodeCtrl()
Dim FileObj As Object
Dim FileSysObj As Object
Dim FolderObj As Object
Dim Path As String
Application.ScreenUpdating = False
Application.EnableEvents = False
' ### Change to directory containing your Excel workbooks
' Note: trailing "\" is assumed by later code
Path = ThisWorkbook.Path & "\TestFiles\"
Set FileSysObj = CreateObject("Scripting.FileSystemObject")
Set FolderObj = FileSysObj.GetFolder(Path)
For Each FileObj In FolderObj.Files
If LCase(Right(FileObj.Name, 4)) = ".xls" Then
Call DeleteCodeSingleWbk(Path & FileObj.Name)
End If
Next
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Sub DeleteCodeSingleWbk(ByVal WbkName As String)
Dim CodeLineCrnt As Long
Dim InxC As Long
Dim NumCodeLines As Long
Dim VBC As VBComponent
Dim VBCType As Long
Dim VBP As VBProject
Dim VBMod As CodeModule
Dim Wbk As Workbook
Err.Clear
' Switch off normal error handling in case attempt to open workbook fails
On Error Resume Next
' Second parameter = False means links will not be updated since not interested in data
' Third parameter = False mean open for updating
Set Wbk = Workbooks.Open(WbkName, False, False)
' Restore normal error handling.
On Error GoTo 0
If Err.Number <> 0 Then
On Error Resume Next
' In case partially open
Wbk.Close SaveChanges:=False
On Error GoTo 0
Else
Set VBP = Wbk.VBProject
' Process components in reverse sequence because deleting a component
' will change the index numbers of components below it.
For Each VBC In VBP.VBComponents
VBCType = VBC.Type
If VBCType = vbext_ct_StdModule Or VBCType = vbext_ct_ClassModule Then
' Component is a module and can be removed
VBP.VBComponents.Remove VBC
ElseIf VBCType = vbext_ct_Document Then
' Component can have a code module which can be cleared
Set VBMod = VBC.CodeModule
NumCodeLines = VBMod.CountOfLines
If NumCodeLines > 0 Then
Call VBMod.DeleteLines(1, NumCodeLines)
End If
End If
Next
Wbk.Close SaveChanges:=True
End If
End Sub
Sub ListComponentsCtrl()
Dim BkUpFileObj As Object
Dim FileObj As Object
Dim FileSysObj As Object
Dim FolderObj As Object
Dim Path As String
Application.ScreenUpdating = False
Application.EnableEvents = False
' ### Change to directory containing your Excel workbooks
' Note: trailing "\" is assumed by later code
Path = ThisWorkbook.Path & "\TestFiles\"
Set FileSysObj = CreateObject("Scripting.FileSystemObject")
Set FolderObj = FileSysObj.GetFolder(Path)
' Second parameter = False means existing file will not be overwritten
' Third parameter = False means ASCII file will be created.
Set BkUpFileObj = FileSysObj.CreateTextFile(Path & "BkUp " & Format(Now(), "yymmyy hhmm") & ".txt", _
False, False)
For Each FileObj In FolderObj.Files
If LCase(Right(FileObj.Name, 4)) = ".xls" Then
Call ListComponentsSingleWbk(Path & FileObj.Name, BkUpFileObj)
End If
Next
BkUpFileObj.Close
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Sub ListComponentsSingleWbk(ByVal WbkName As String, ByRef BkUpFileObj As Object)
Dim CodeLineCrnt As Long
Dim InxC As Long
Dim NumCodeLines As Long
Dim VBC As VBComponent
Dim VBCType As Long
Dim VBP As VBProject
Dim VBMod As CodeModule
Dim Wbk As Workbook
Call BkUpFileObj.WriteLine("Workbook " & WbkName)
Err.Clear
' Switch off normal error handling in case attempt to open workbook fails
On Error Resume Next
' Second parameter = False means links will not be updated since not interested in data
' Third parameter = True mean open read only
Set Wbk = Workbooks.Open(WbkName, False, True)
' Restore normal error handling.
On Error GoTo 0
If Err.Number <> 0 Then
Call BkUpFileObj.WriteLine(" Unable to open workbook: " & Err.desc)
Else
Set VBP = Wbk.VBProject
For InxC = 1 To VBP.VBComponents.Count
Set VBC = VBP.VBComponents(InxC)
VBCType = VBC.Type
If VBCType = vbext_ct_StdModule Or VBCType = vbext_ct_ClassModule Or _
VBCType = vbext_ct_Document Then
' Component can have a code module
Set VBMod = VBC.CodeModule
NumCodeLines = VBMod.CountOfLines
If NumCodeLines = 0 Then
Call BkUpFileObj.WriteLine(" No code associated with " & _
VBCTypeNumToName(VBCType) & " " & VBC.Name)
Else
Call BkUpFileObj.WriteLine(" Code within " & _
VBCTypeNumToName(VBCType) & " " & VBC.Name)
For CodeLineCrnt = 1 To NumCodeLines
Call BkUpFileObj.WriteLine(" " & VBMod.Lines(CodeLineCrnt, 1))
Next
End If
End If
Next
End If
Wbk.Close SaveChanges:=False
End Sub
Function VBCTypeNumToName(ByVal VBCType As Long) As String
Select Case VBCType
Case vbext_ct_StdModule ' 1
VBCTypeNumToName = "Module"
Case vbext_ct_ClassModule ' 2
VBCTypeNumToName = "Class Module"
Case vbext_ct_MSForm ' 3
VBCTypeNumToName = "Form"
Case vbext_ct_ActiveXDesigner ' 11
VBCTypeNumToName = "ActiveX Designer"
Case vbext_ct_Document ' 100
VBCTypeNumToName = "Document Module"
End Select
End Function
Given you couldnt get Tony's code to work try this version:
Change "C:\temp" to the path of your choice
All xls files will be opened, saved as "orginalfilename_no_code.xlsx" and the prior version will be removed
Sub CullCode()
Dim StrFile As String
Dim strPath As String
Dim WB As Workbook
strPath = "c:\temp\"
StrFile = Dir(strPath & "*.xls*")
With Application
.DisplayAlerts = False
.ScreenUpdating = False
.EnableEvents = False
End With
Do While Len(StrFile) > 0
Set WB = Workbooks.Open(strPath & StrFile)
WB.SaveAs strPath & StrFile & "_no_code.xlsx", 51
WB.Close False
Kill strPath & StrFile
StrFile = Dir
Loop
With Application
.DisplayAlerts = True
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub