Adding Hyperlink via macro - vba
I have a workbook (let's call it "GENERAL") which activates via macro a lot of other workbooks in a specific folder and pulls specific data over to GENERAL in order to build a total sum. I would like to automatically create a hyperlink on one of the copied values leading back to the specific workbook in order to be able to trace errors quickly. My code is the following:
MyPath = "Y:\test_workbook"
MyFile = Dir(MyPath)
Do While MyFile <> ""
If MyFile Like "*.xls" Or MyFile Like "*.xlsx" Then
Workbooks.Open MyPath & MyFile
Sheets(1).Select
Range("A1") = myValue
Range("A1") = CDate(myValue)
a = Range("A2").Value ' here file-title
b = Range("A3").Value
c = Range("A4").Value
f = Range("B1").Value
ActiveWorkbook.Close SaveChanges:=True
End If
Workbooks("GENERAL.xlsm").Activate
Sheets("total").Cells(i, j) = a
Sheets("total").Cells(i, j + b) = c
i = i + 1
MyFile = Dir
Loop
So: I want to create a Hyperlink in the workbook GENERAL which is mapped on the cell containing the A2-cell-information of the correlated workbook which provides the information and leads back to it.
Does anybody have a clue how this is done ?
I'm afraid I don't understand your context, but here's how you create hyperlink in Excel VBA:
With worksheet
.Hyperlinks.Add Anchor:=.Range("A2"), _
Address:="http://www.stackoverflow.com", _
ScreenTip:="Stack Overflow", _
TextToDisplay:="StackOverflow"
End With
Related
Excel VBA - Running a Macro against all files in a folder [duplicate]
I have a folder where I receive 1000+ excel files on daily bases they all are same format and structure. What I want to do is run a macro on all 100+ files on daily bases ? Is there way to automate this ? So I can keep running that same macro on 1000+ files daily.
Assuming that you put your files in "Files" directory relative to your master workbook your code might look like this: Sub ProcessFiles() Dim Filename, Pathname As String Dim wb As Workbook Pathname = ActiveWorkbook.Path & "\Files\" Filename = Dir(Pathname & "*.xls") Do While Filename <> "" Set wb = Workbooks.Open(Pathname & Filename) DoWork wb wb.Close SaveChanges:=True Filename = Dir() Loop End Sub Sub DoWork(wb As Workbook) With wb 'Do your work here .Worksheets(1).Range("A1").Value = "Hello World!" End With End Sub In this example DoWork() is your macro that you apply to all of your files. Make sure that you do all your processing in your macro is always in the context of the wb (currently opened workbook). Disclaimer: all possible error handling skipped for brevity.
A part of the question might be how do I run this on 1000 files?... Do I have to add this macro to all 1000 workbooks? One way to do this is to add your macro's centrally to the file PERSONAL.XLSB (sometimes the extension might be different). This file will be loaded in the background every time you start Excel and makes your macro's available at any time. Initially the PERSONAL.XLSB file will NOT be there. To automatically create this file, just start recording a "dummy" macro (with the record button on the left-bottom of a spreadsheet) and select "Personal Macro Workbook" to store it in. After recording your macro, you can open the VBA editor with Alt+F11 and you will see the PERSONAL.XLSB file with the "dummy" recorded macro. I use this file to store loads of general macro's which are always available, independent of which .xlsx file I have open. I have added these macro's to my own menu ribbon. One disadvantage of this common macro file is that if you launch more than one instance of Excel, you will get an error message that the PERSONAL.XLSB file is already in use by Excel instance Nr. 1. This is no problem as long as you do not add new macro's at this moment.
Thank you very much for this Sub ProcessFiles() Dim Filename, Pathname As String Dim wb As Workbook Pathname = ActiveWorkbook.Path & "C:\Users\jkatanan\Desktop\20170206Glidepath\V37\" Filename = Dir(Pathname & "*.xls") Do While Filename <> "" Set wb = Workbooks.Open(Pathname & Filename) BSAQmacro wb wb.Close SaveChanges:=True Filename = Dir() Loop End Sub Sub DoWork(wb As Workbook) With wb 'Do your work here .Worksheets(1).Range("A1").Value = "Hello World!" End With End Sub
Sub ProcessFiles() Dim Filename, Pathname As String Dim wb As Workbook Pathname = ActiveWorkbook.Path & "\C:\Users\20098323\Desktop\EXCL\" Filename = Dir(Pathname & "*.xlsx") Do While Filename <> "" Set wb = Workbooks.Open(Pathname & Filename) DoWork wb wb.Close SaveChanges:=True Filename = Dir() Loop End Sub Sub DoWork(wb As Workbook) With wb 'Do your work here .Worksheets(1).Range("A1").Value = "Hello World!" End With End Sub While running this code its showing bad file name or number. i have stored my all file in ("\C:\Users\20098323\Desktop\EXCL\") EXCL folder
Instead of passing the values to DoWork one can also run the jobs in Processfiles(). Sub ProcessFiles() Dim Filename, Pathname As String Dim wb1 As Workbook Dim wb2 As Workbook Dim Sheet As Worksheet Dim PasteStart As Range Dim Counter As Integer Set wb1 = ActiveWorkbook Set PasteStart = [RRimport!A1] Pathname = ActiveWorkbook.Path & "\For Macro to run\" Filename = Dir(Pathname & "*.xls") Do While Filename <> "" Set wb2 = Workbooks.Open(Pathname & Filename) For Each Sheet In wb2.Sheets With Sheet.UsedRange .Copy PasteStart Set PasteStart = PasteStart.Offset(.Rows.Count) End With Next Sheet wb2.Close Filename = Dir() Loop End Sub
This isn't an exact answer to the question, since I was just trying to run a single script on any file that I opened and couldn't get that to work, but I thought this might help others like me. It worked once I moved the code into a Module in the Visual Basic for Applications dialog box (go to "Insert" then "Module"). Once I added my VBA code to a module, I was able to open any other file in Excel (even a CSV file) and go to Macros, and run the Macro from the other file (that contains the Module with the code) on the file that I had open.
Thanks Peterm!! Actually, I did my macro using exactly the same code you posted (process_fiels and dowork). It worked brilliant!! (before my question) Each of my 1000 workbooks has 84 worksheets. My own macro (which finally works!) splits each workbook into 85 different files (the original + a short version of each worksheet saved as an individual file). That leaves me with 1000 files + 1000x85 in the same folder, and that would be really hard to sort out. What I really need is for Process_Files to take the first file, create a folder with the name of the first file, move the first file to the folder with ist name, then run my macro (in the folder named after the first file in the newly created folder...), go back and take the second file, create a folder with the name of the second file, move the second file to the folder with ist name, then run my macro (in the folder named after the second file in the newly created folder...), etc... At the end, I should have moved all files into folders with the same name as the files, and the contents of the original \Files\ folder would be 1000 folders with the name of the original files, containgin the original files + 84 files which my own macro already does. Maybe it is easier with the code: Sub ProcessFiles() Dim Filename, Pathname As String Dim wb As Workbook Pathname = ActiveWorkbook.Path & "\Files\" Filename = Dir(Pathname & "*.xls") Do While Filename <> "" (Here, it should read the file name, create a folder with the file name, move the file into this newly created folder) Set wb = Workbooks.Open(Pathname & Filename) <- open file, just as is. DoWork wb <- do my macro,just as is wb.Close SaveChanges:=False <- not save, to keep the original file (go back to the original \Files\ folder) Filename = Dir() <- Next file, just as is Loop End Sub Sub DoWork(wb As Workbook) With wb MyMacro End With End Sub Many thanks, this site is great! __________________edit, the macro now works _________________________ As you can see, I am no VBA expert, but the macro finally works. The code is not neat at all, I am no SW programmer. Here it is, it might help some one some day. Sub ProcessFiles_All() Dim Filename, Pathname, NewPath, FileSource, FileDestination As String Dim wb As Workbook Pathname = ActiveWorkbook.Path & "\Files\" Filename = Dir(Pathname & "*.csv") Do While Filename <> "" NewPath = Pathname & Left(Filename, 34) & "\" On Error Resume Next MkDir (NewPath) On Error GoTo 0 Set wb = Workbooks.Open(Pathname & Filename) DoWorkPlease wb ' <------------ It is important to say please!! On Error Resume Next wb.Close SaveChanges:=False if Err.Number <> 0 then ‘Error handler needed here End if Filename = Dir() Loop End Sub Sub DoWorkPlease(wb As Workbook) With wb ‘ Since my application has over 1800 cells for each column and it is time consuming ‘ I use a “testing mode” were I only play with 18 values. Dim TestingMode As Integer Dim ThisRange(1 To 4) As Variant TestingMode = 0 If TestingMode = 1 Then ThisRange(1) = "B2:CG18" ThisRange(2) = "CT2:CT18" ThisRange(3) = "CH2:CN18" ThisRange(4) = "CN2:CS18" Rows("19:18201").Select Selection.Delete Shift:=xlUp End If If TestingMode = 0 Then ThisRange(1) = "B2:CG18201" ThisRange(2) = "CT2:CT18201" ThisRange(3) = "CH2:CN18201" ThisRange(4) = "CN2:CS18201" End If ‘ speed up the macro, turn off updating and alerts Application.ScreenUpdating = False Application.DisplayAlerts = False ‘ Here is my code that manipulates the cell values from digits (values read by sensors need to be “translated” into real world values. Code not here actually. ‘Then I copy the whole thing into just numbers, there are no longer formulas, easier to work this way. '_____________________________________ 'Get just values - no more formulas Sheets.Add After:=Sheets(Sheets.Count) Sheets("Sheet1").Select Columns("A:CT").Select Selection.Copy Sheets("Sheet2").Select Columns("A:A").Select Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False Application.CutCopyMode = False Selection.NumberFormat = "0" With Selection .HorizontalAlignment = xlCenter .VerticalAlignment = xlBottom .WrapText = False .Orientation = 0 .AddIndent = False .IndentLevel = 0 .ShrinkToFit = False .ReadingOrder = xlContext .MergeCells = False End With ‘ Then I save this new workbook into a folder with its own name (and under the folder \FILES\ '_____________________________________ 'Save the work under its own folder Dim CleanName, CleanPath, CleanNewName As Variant CleanPath = ActiveWorkbook.Path CleanName = ActiveWorkbook.Name CleanName = Left(CleanName, 34) ‘I take out the extension CleanPath = CleanPath + "\" + CleanName CleanNewName = CleanPath + "\" + CleanName CleanNewName = CleanNewName + "_clean.csv" ‘ and I add “clean” to have a different name now. On Error Resume Next ActiveWorkbook.SaveAs Filename:=CleanNewName, FileFormat:=xlCSV, CreateBackup:=False ‘If there is an error I create an empty folder with the name of the file to know which file needs rework. If Err.Number <> 0 Then MkDir (CleanPath + "_error_" + CleanName) End If 'Resume Next ActiveSheet.Move _ After:=ActiveWorkbook.Sheets(1) ‘ Then I split the workbook into individual files with the data I need for individual sensors. ‘ Here are the individual ranges I need for each file. Since I have over 1000 files, it is worth the effort. '_______________ the Split!!______________________________ Dim Col(1 To 98) As Variant Col(1) = "A:A,B:B,CH:CH,CN:CN,CT:CT" Col(2) = "A:A,C:C,CH:CH,CN:CN,CT:CT" Col(3) = "A:A,D:D,CH:CH,CN:CN,CT:CT" Col(4) = "A:A,E:E,CH:CH,CN:CN,CT:CT" Col(5) = "A:A,F:F,CH:CH,CN:CN,CT:CT" Col(6) = "A:A,G:G,CH:CH,CN:CN,CT:CT" Col(7) = "A:A,H:H,CH:CH,CN:CN,CT:CT" Col(8) = "A:A,I:I,CH:CH,CN:CN,CT:CT" Col(9) = "A:A,J:J,CH:CH,CN:CN,CT:CT" Col(10) = "A:A,K:K,CH:CH,CN:CN,CT:CT" Col(11) = "A:A,L:L,CH:CH,CN:CN,CT:CT" Col(12) = "A:A,M:M,CH:CH,CN:CN,CT:CT" Col(13) = "A:A,N:N,CH:CH,CN:CN,CT:CT" Col(14) = "A:A,O:O,CH:CH,CN:CN,CT:CT" Col(15) = "A:A,P:P,CI:CI,CO:CO,CT:CT" Col(16) = "A:A,Q:Q,CI:CI,CO:CO,CT:CT" Col(17) = "A:A,R:R,CI:CI,CO:CO,CT:CT" Col(18) = "A:A,S:S,CI:CI,CO:CO,CT:CT" Col(19) = "A:A,T:T,CI:CI,CO:CO,CT:CT" Col(20) = "A:A,U:U,CI:CI,CO:CO,CT:CT" Col(21) = "A:A,V:V,CI:CI,CO:CO,CT:CT" Col(22) = "A:A,W:W,CI:CI,CO:CO,CT:CT" Col(23) = "A:A,X:X,CI:CI,CO:CO,CT:CT" Col(24) = "A:A,Y:Y,CI:CI,CO:CO,CT:CT" Col(25) = "A:A,Z:Z,CI:CI,CO:CO,CT:CT" Col(26) = "A:A,AA:AA,CI:CI,CO:CO,CT:CT" Col(27) = "A:A,AB:AB,CI:CI,CO:CO,CT:CT" Col(28) = "A:A,AC:AC,CI:CI,CO:CO,CT:CT" Col(29) = "A:A,AD:AD,CJ:CJ,CP:CP,CT:CT" Col(30) = "A:A,AE:AE,CJ:CJ,CP:CP,CT:CT" Col(31) = "A:A,AF:AF,CJ:CJ,CP:CP,CT:CT" Col(32) = "A:A,AG:AG,CJ:CJ,CP:CP,CT:CT" Col(33) = "A:A,AH:AH,CJ:CJ,CP:CP,CT:CT" Col(34) = "A:A,AI:AI,CJ:CJ,CP:CP,CT:CT" Col(35) = "A:A,AJ:AJ,CJ:CJ,CP:CP,CT:CT" Col(36) = "A:A,AK:AK,CJ:CJ,CP:CP,CT:CT" Col(37) = "A:A,AL:AL,CJ:CJ,CP:CP,CT:CT" Col(38) = "A:A,AM:AM,CJ:CJ,CP:CP,CT:CT" Col(39) = "A:A,AN:AN,CJ:CJ,CP:CP,CT:CT" Col(40) = "A:A,AO:AO,CJ:CJ,CP:CP,CT:CT" Col(41) = "A:A,AP:AP,CJ:CJ,CP:CP,CT:CT" Col(42) = "A:A,AQ:AQ,CJ:CJ,CP:CP,CT:CT" Col(43) = "A:A,AR:AR,CK:CK,CQ:CQ,CT:CT" Col(44) = "A:A,AS:AS,CK:CK,CQ:CQ,CT:CT" Col(45) = "A:A,AT:AT,CK:CK,CQ:CQ,CT:CT" Col(46) = "A:A,AU:AU,CK:CK,CQ:CQ,CT:CT" Col(47) = "A:A,AV:AV,CK:CK,CQ:CQ,CT:CT" Col(48) = "A:A,AW:AW,CK:CK,CQ:CQ,CT:CT" Col(49) = "A:A,AX:AX,CK:CK,CQ:CQ,CT:CT" Col(50) = "A:A,AY:AY,CK:CK,CQ:CQ,CT:CT" Col(51) = "A:A,AZ:AZ,CK:CK,CQ:CQ,CT:CT" Col(52) = "A:A,BA:BA,CK:CK,CQ:CQ,CT:CT" Col(53) = "A:A,BB:BB,CK:CK,CQ:CQ,CT:CT" Col(54) = "A:A,BC:BC,CK:CK,CQ:CQ,CT:CT" Col(55) = "A:A,BD:BD,CK:CK,CQ:CQ,CT:CT" Col(56) = "A:A,BE:BE,CK:CK,CQ:CQ,CT:CT" Col(57) = "A:A,BF:BF,CL:CL,CR:CR,CT:CT" Col(58) = "A:A,BG:BG,CL:CL,CR:CR,CT:CT" Col(59) = "A:A,BH:BH,CL:CL,CR:CR,CT:CT" Col(60) = "A:A,BI:BI,CL:CL,CR:CR,CT:CT" Col(61) = "A:A,BJ:BJ,CL:CL,CR:CR,CT:CT" Col(62) = "A:A,BK:BK,CL:CL,CR:CR,CT:CT" Col(63) = "A:A,BL:BL,CL:CL,CR:CR,CT:CT" Col(64) = "A:A,BM:BM,CL:CL,CR:CR,CT:CT" Col(65) = "A:A,BN:BN,CL:CL,CR:CR,CT:CT" Col(66) = "A:A,BO:BO,CL:CL,CR:CR,CT:CT" Col(67) = "A:A,BP:BP,CL:CL,CR:CR,CT:CT" Col(68) = "A:A,BQ:BQ,CL:CL,CR:CR,CT:CT" Col(69) = "A:A,BR:BR,CL:CL,CR:CR,CT:CT" Col(70) = "A:A,BS:BS,CL:CL,CR:CR,CT:CT" Col(71) = "A:A,BT:BT,CM:CM,CS:CS,CT:CT" Col(72) = "A:A,BU:BU,CM:CM,CS:CS,CT:CT" Col(73) = "A:A,BV:BV,CM:CM,CS:CS,CT:CT" Col(74) = "A:A,BW:BW,CM:CM,CS:CS,CT:CT" Col(75) = "A:A,BX:BX,CM:CM,CS:CS,CT:CT" Col(76) = "A:A,BY:BY,CM:CM,CS:CS,CT:CT" Col(77) = "A:A,BZ:BZ,CM:CM,CS:CS,CT:CT" Col(78) = "A:A,CA:CA,CM:CM,CS:CS,CT:CT" Col(79) = "A:A,CB:CB,CM:CM,CS:CS,CT:CT" Col(80) = "A:A,CC:CC,CM:CM,CS:CS,CT:CT" Col(81) = "A:A,CD:CD,CM:CM,CS:CS,CT:CT" Col(82) = "A:A,CE:CE,CM:CM,CS:CS,CT:CT" Col(83) = "A:A,CF:CF,CM:CM,CS:CS,CT:CT" Col(84) = "A:A,CG:CG,CM:CM,CS:CS,CT:CT" ‘ I want to split 84 new files, so for testing I use only 1, and for the real thing I go with 84 Dim CounterMode As Integer If TestingMode = 1 Then CounterMode = 1 Else CounterMode = 84 For i = 1 To CounterMode ‘ this code takes the columns need, and paste it into a new workbook. Sheets("Sheet1").Select Cells.Select Selection.ClearContents Range("A1").Activate Sheets(2).Select Range(Col(i)).Select Selection.Copy Sheets("Sheet1").Select ActiveSheet.Paste Application.CutCopyMode = False With Selection .HorizontalAlignment = xlCenter .VerticalAlignment = xlBottom .WrapText = False .Orientation = 0 .AddIndent = False .IndentLevel = 0 .ShrinkToFit = False .ReadingOrder = xlContext .MergeCells = False End With Columns("A:E").EntireColumn.AutoFit ‘ Save the individual file '_____________save the work________________ Dim ThePath, TheName, TheSwitch As String ThePath = ActiveWorkbook.Path + “\” TheName = Left(ActiveWorkbook.Name, 34) ‘ take out the extension from the name ThePath = ThePath + TheName TheSwitch = Cells(3, 2) ‘ In Cell (3,2) I have the name of the individual name, so I added to the file name. TheName = ThePath + "_" + TheSwitch + ".xls" Range("A1").Select Sheets("Sheet1").Select Sheets("Sheet1").Copy Dim SheetName As Variant ‘ I name Sheets(1) as Sheet1, since the original sheet has the name and date of the test. ‘ I do this to have the same name on all file in order to do a plot, then I rename the sheet with the ‘ original name SheetName = ActiveSheet.Name ActiveWorkbook.Sheets(1).Name = "Sheet1" ‘ here is the plot Columns("A:E").EntireColumn.AutoFit Columns("B:E").Select ActiveSheet.Shapes.AddChart.Select ActiveChart.SetSourceData Source:=Range("'Sheet1'!$B:$E") ActiveChart.ChartType = xlXYScatterLinesNoMarkers ActiveWorkbook.Sheets(1).Name = SheetName ‘save On Error Resume Next ActiveWorkbook.SaveAs Filename:=TheName, FileFormat:=56, CreateBackup:=False If Err.Number <> 0 Then MkDir (ThePath + "_error_" + TheName) End If ActiveWorkbook.Close Next i '____________________That was the Split__________________________________ ' Turn on screenupdating: Application.ScreenUpdating = True Application.DisplayAlerts = True Range("A1").Select End With End Sub
Copy row until cell not empty
Thanks for any help you can provide. I have managed using a macro to search through folders and sub folders and hyperlink them and display Folder 1 in column A the files that is in the folder in column B. There is about 200 folders and some 1600 files. Anyway I want to be able to place a button and attach a macro to that button that will enable only the folder and files names be to copy from that buttons position. I was thinking of placing that button in column D directly across from the folder name in column A
The code does something similar to what you appear to want. It recursively (see TraversePath subroutine) finds all the paths and files in them and prints them out to "Sheet1" in the same manner as the image you posted with your question: the file folder names are written out to column 'A' (as a hyperlink), the files in that folder are written out to column 'B' (again as hyperlinks) and a button is placed in column 'C'. Modify 'CreateDirSheet' with the root or top directory for which you want all sub-folders and files printed out in 'Sheet1'. The '1' argument to 'TraversePath' is the row # of where to start printing out the folders/files in 'Sheet'. The TraversePath subroutine places the buttons and identifies the macro handler that is processed when the button is pressed. Two arguments are passed to that routine: the name of the sheet (it'll be 'Sheet1' in this case) and the row number from where the folder is given in column 'A'. When a button is pressed the handler prompts the user for the destination path and goes down the list in column 'B' copying all the files from the source folder (in column 'A') to the destination folder provided by the user. It's probably not entirely what you're after but should be a good starting point to get the functionality you want. Option Explicit ' Button event handler Sub CopyDirBtn(shtName As String, rs As String) Dim sht As Worksheet Set sht = Worksheets(shtName) ' Get the destination path (where to copy files) from user Dim dpath As String, spath As String Dim fdialog As FileDialog Set fdialog = Application.FileDialog(msoFileDialogFolderPicker) With fdialog .Title = "Select a Folder" .AllowMultiSelect = False .InitialFileName = Application.DefaultFilePath If .Show <> 0 Then dpath = .SelectedItems(1) Else Exit Sub End If End With ' Copy all files Dim r As Integer: r = CInt(rs) With sht spath = .Cells(r, "A") r = r + 1 Do While .Cells(r, "B") <> "" FileCopy spath & .Cells(r, "B"), dpath & "\" & .Cells(r, "B") r = r + 1 Loop End With End Sub ' Populate sheet with folder/link links and buttons Sub TraversePath(path As String, r As Integer) Dim currentPath As String, directory As Variant Dim dirCollection As Collection Set dirCollection = New Collection currentPath = Dir(path, vbDirectory) Dim sht As Worksheet Set sht = Worksheets("Sheet1") With sht 'Add directory and hyperlink to sheet .Hyperlinks.Add Anchor:=.Cells(r, "A"), _ Address:=path, _ TextToDisplay:=path ' Add copy button Dim copyBtn As Button Set copyBtn = .Buttons.Add(Cells(r, "C").Left, _ Cells(r, "C").Top, 100#, 14#) With copyBtn .Caption = "Copy Files" .Name = "copyBtn_" & r .Locked = False .OnAction = "'CopyDirBtn """ & sht.Name & """, """ & r & """'" End With ' Add files and hyperlinks to sheet r = r + 1 Do Until currentPath = vbNullString If Left(currentPath, 1) <> "." And _ (GetAttr(path & currentPath) And vbDirectory) = vbDirectory Then dirCollection.Add currentPath Else If currentPath <> "." And currentPath <> ".." Then .Hyperlinks.Add Anchor:=.Cells(r, "B"), _ Address:=path, _ TextToDisplay:=currentPath r = r + 1 End If End If currentPath = Dir() Loop End With 'process remaining directories For Each directory In dirCollection TraversePath path & directory & "\", r Next directory End Sub ' This is the main macro that populates the sheet ' Modify first parameter so it's your root folder path Sub CreateDirSheet() TraversePath "D:\tmp\", 1 End Sub
In VBA, my VLOOKUP needs to Update Values
I'm writing a script that requires opening a second workbook and running a VLOOKUP in the second workbook. It works perfectly when the filename of the second workbook is "testlookup.xlsx" but when I changed the filename to "hippity hop 1251225253.xlsx", it opens a window that says "Update Values: 1251225253" and then the VLOOKUP fails. How can I get the code to work regardless of the filename? fpath = Application.GetOpenFilename(, , "Select the CMS All Assets exported CSV") fname = Dir(fpath) Workbooks.Open (fpath) Set openedBook = Application.ActiveWorkbook Set assetBook = openedBook.Worksheets(1) ActiveWindow.WindowState = xlMinimized checkWkbk.Activate With dupeSheet 'determine last row lr = .Cells(Rows.count, 1).End(xlUp).Row 'vlookup from C2:CEnd .Range(.Cells(2, 3), .Cells(lr, 3)).FormulaR1C1 = _ "=VLOOKUP(RC[-2], " & CStr(fname) & "!C1:C2, 2, FALSE)" End With
If your description of the filenames is correct, the problem is that you're using a file name with space characters in it, which is throwing the VLookup off. You need to put single-quote characters around the file name in the formula, thus: "=VLOOKUP(RC[-2], '" & CStr(fname) & "'!C1:C2, 2, FALSE)" I may be off base with this bit, since you said it works when you don't have spaces in the file names, but you should also include the worksheet name in the formula string, so your formula would look more like this: "=VLOOKUP(RC[-2], '[" & CStr(fname) & "]" & assetBook.name & "'!C1:C2, 2, FALSE)"
Part of what may be happening is you use the ActiveWorkbook to find the workbook you need versus finding the workbook by the correct name. I use the below subroutine for this purpose: Sub Get_Workbook_Object(sPath As String, wbHolder As Workbook) Dim wb As Workbook If Len(sPath) > 0 Then ThisWorkbook.FollowHyperlink (sPath) Else Exit Sub End If For Each wb In Workbooks If wb.FullName = sPath Then Set wbHolder = wb Exit Sub End If Next End Sub To use this code you could add the subroutine to your module and then call it with something like: Get_Workbook_Object fPath, openedBook Also Dir() isn't going to return a fullpath, it is only going to return the appropriate filename. For example, it may return "Hippity Hop.xlsx" instead of "C:Users\Hippity Hop.xlsx" where the first part is the actual filepath. You may want to use something like this instead: With Application.FileDialog(msoFileDialogFilePicker) .Title = "Please select the CMS All Assets exported CSV" .Show If .SelectedItems.Count = 1 Then fpath = .SelectedItems(1) Else MsgBox "Please choose at least one file" Exit Sub End If End With This will return the full path of the file.
Making excel macro for file scanning more stable
I was curious if anybody could provide suggestions on how I can make an excel macro more stable. The macro prompts the user for a path to a folder containing files to scan. The macro then iterates for every file in this folder. It opens the excel file, scans Column D for the word fail, then copies that row of data to the data sheet in the excel file where this macro is programmed. For the most part the macro runs perfectly but sometimes I get run time errors or 'excel has stopped working' errors. I can scan through 5000+ files at a time and the macro takes a while to run. Any suggestions would be appreciated. Thanks! Sub findFail() Dim pathInput As String 'path to file Dim path As String 'path to file after being validated Dim fileNames As String 'path to test file Dim book As Workbook 'file being tested Dim sheet As Worksheet 'sheet writting data to Dim sh As Worksheet 'worksheet being tested Dim dataBook As Workbook 'where data is recorded Dim row As Long 'row to start writting data in Dim numTests As Long 'number of files tested Dim j As Long 'counter for number of files tested Dim i As Long 'row currently being tested Dim lastRow As Long 'last row used Dim startTime As Double 'time when program started Dim minsElapsed As Double 'time it took program to end Application.ScreenUpdating = False j = 0 i = 1 row = 2 Set dataBook = ActiveWorkbook Set sheet = Worksheets("Data") sheet.Range("A2:i1000").Clear startTime = Timer '-----Prompt for Path----- pathInput = InputBox(Prompt:="Enter path to files. It must have a \ after folder name.", _ Title:="Single Report", _ Default:="C:\Folder\") If pathInput = "C:\Folder\" Or pathInput = vbNullString Then 'check to make sure path was inputed MsgBox ("Please enter a valid file path and try again.") Exit Sub Else path = pathInput 'path = "C:\Temp\212458481\" ' Path for file location fileNames = Dir(path & "*.xls") 'for xl2007 & "*.xls?" on windows '-----begin testing----- Do While fileNames <> "" 'Loop until filename is blank Set book = Workbooks.Open(path & fileNames) Set sh = book.Worksheets(1) lastRow = sh.UsedRange.Rows(sh.UsedRange.Rows.Count).row If sh.Cells(lastRow, 2).Value - sh.Cells(1, 2).Value >= 0.08333333 Then Do While sh.Range("D" & i).Value <> "" 'loop untile there are no rows left to test If sh.Range("D" & i).Value = "Fail" Then 'record values if test result is false sheet.Range("A" & row).Value = book.Name sheet.Range("B" & row).Value = Format(sh.Range("B" & i).Value - sh.Range("B1").Value, "h:mm:ss") sheet.Range("C" & row).Value = sh.Range("A" & i).Value sheet.Range("D" & row).Value = Format(sh.Range("B" & i).Value, "h:mm:ss") sheet.Range("E" & row).Value = sh.Range("C" & i).Value sheet.Range("F" & row).Value = sh.Range("D" & i).Value sheet.Range("G" & row).Value = sh.Range("E" & i).Value sheet.Range("H" & row).Value = sh.Range("F" & i).Value sheet.Range("I" & row).Value = sh.Range("G" & i).Value row = row + 1 Exit Do End If i = i + 1 Loop j = j + 1 dataBook.Sheets("Summary").Cells(2, 1).Value = j End If book.Close SaveChanges:=False fileNames = Dir() i = 1 Loop numTests = j Worksheets("Summary").Cells(2, "A").Value = numTests minsElapsed = Timer - startTime Worksheets("Summary").Cells(2, "B").Value = Format(minsElapsed / 86400, "hh:mm:ss") End If End Sub
Without the same dataset as you we, can not definitively supply an answer but I can recommend the below which is related to the error you are seeing. Try freeing/destroying the references to book and sh. You have a loop that sets them:- Do While fileNames <> "" 'Loop until filename is blank Set book = Workbooks.Open(path & fileNames) Set sh = book.Worksheets(1) However the end of the loop does not clear them, ideally it should look as below:- Set sh = Nothing Set book = Nothing Loop This is a better way to handle resources and should improve memory usage. As a poor example, without it your code is saying, sh equals this, now it equals this instead, now it equals this instead, now it equals this instead, etc... You end up with the previous reference that was subsequently overwritten being a sort of orphaned object that is holding some space in memory.
Depending on your case, you may use the following to make it faster -by turning off excel processes that you don't really need at the time of your macro execution- Sub ExcelBusy() With Excel.Application .Cursor = xlWait .ScreenUpdating = False .DisplayAlerts = False .StatusBar = False .Calculation = xlCalculationManual .EnableEvents = False End With End Sub In your sub Dim startTime As Double 'time when program started Dim minsElapsed As Double 'time it took program to end Call ExcelBusy ... As a comment, you never set back screenupdating to true in your sub, that may lead to strange behavior in excel, you should turn everything to default after you are done with your stuff. OT: Some processes can't be optimized any further -sometimes-, by what you are saying -scanning over 5k files?- surely it's going to take a time, you need to work in how to communicate the user that is going to take a while instead -perhaps an application status bar message or a user form showing process?-.
Save excel worksheet to specific folder and filename based on same cell
I am trying to save a copy of a worksheet to a specific folder based on cell B8 and name the .xlsx file based on the cell range B8 too. For example, a user first creates a new folder named "test1", & then enters this folder name/text into cell "B8". He/she will activate the macro after completing their work on the worksheet, & it will save a copy to the folder named "test1" and name the .xlsx file as "test1". (So the .xlsx file will be named as "testfolder1" and the folder where it is stored is also called "test1") I am using the following code to save a copy of the worksheet to a folder. Just can't figure out how to include the cell B8 into the SaveAs line. Too new with VB to figure it out. Sub SaveForm() exampleForm = Range("B8").Value Application.ScreenUpdating = False Application.DisplayAlerts = False ActiveSheet.Copy With ActiveWorkbook.ActiveSheet .Range("42:" & Rows.Count).EntireRow.Delete xlShiftDown .Range(.Cells(1, "J"), .Cells(1, Columns.Count)).EntireColumn.Delete xlToRight .Parent.SaveAs "C:\Users\JohnSmith\Desktop\ExtractedWorksheet\" & exampleForm & ".xlsx" .Parent.Close False End With End Sub Appreciate any input and hopefully my ending goal is understandable. -Thanks!
I think this is what you're after, give it a try: Sub SaveForm() Static Path as string Static FileName as string if len(Path) = 0 then Path = Range("B8") if right(Path,1) <> "\" then 'make sure the path is "\" terminated Path = Path & "\" End if else FileName = Range("B8") 'Application.ScreenUpdating = False Application.DisplayAlerts = False ActiveSheet.Copy 'not sure why you're doing this, but do so if it makes sense elsewhere in your code With ActiveWorkbook.ActiveSheet .Range("42:" & Rows.Count).EntireRow.Delete xlShiftDown .Range(.Cells(1, "J"), .Cells(1, Columns.Count)).EntireColumn.Delete xlToRight .Parent.SaveAs "C:\Users\JohnSmith\Desktop\ExtractedWorksheet\" & Path & _ FileName & ".xlsx" .Parent.Close False End With Path = "" FileName = "" End if End Sub If you call this code from your worksheet_OnChange event, then when cell B8 is updated, it will: check to see if you have a Path stored. If not, assume this is the Path If you have a Path already, assume this is the FileName and save it. Leave the 'Application.ScreenUpdating` commented out until everything is working OK, then put it back in. Makes figuring out what's going on much easier. UPDATE based on your latest comment on OP: Sub SaveForm() 'Application.ScreenUpdating = False Application.DisplayAlerts = False ActiveSheet.Copy 'not sure why you're doing this, but do so if it makes sense elsewhere in your code With ActiveWorkbook.ActiveSheet .Range("42:" & Rows.Count).EntireRow.Delete xlShiftDown .Range(.Cells(1, "J"), .Cells(1, Columns.Count)).EntireColumn.Delete xlToRight .Parent.SaveAs "C:\Users\JohnSmith\Desktop\ExtractedWorksheet\" & _ Range("B8") & "\" & FileName & ".xlsx" .Parent.Close False End With Path = "" FileName = "" End Sub
Here is one that I have created for a project that I worked on. I first named a cell (through Excel user interface-formula-name manager-define name) and called it prform_prnumber. I passed the value in that to a variant variable in vba and called it prnumber. I then used that variable as name in the exporttopdf method. I kept the path as that of the workbook. This code will run (once the button is clicked) in the active worksheet: Sub exporttopdf() Dim prnumber As Variant Set prnumber = ActiveWorkbook.Names("prform_prnumber").RefersToRange ActiveSheet.ExportAsFixedFormat xlTypePDF, ActiveWorkbook.Path & "/" & filesavename & ".pdf", , , False End Sub