Macro Produces Different Results If Clicking Screen While Running - vba

I have two files (Microsoft Excel 97-2003 Worksheets) that contain multiple fields of data. A macro I have will open both files, copy the data (without headers) from one of them, and paste that data underneath the data in the other file. Once the data has been combined, I save the combined file as a new workbook. A somewhat stripped down version of the code I have is below (sorry it's inefficient; I had only just begun VBA when I wrote it):
Sub Combine()
' Combine Macro
' Turn some things off to speed things up
Application.ScreenUpdating = False
Application.StatusBar = False
Application.EnableEvents = False
' Set to directory where files to combine exist
ChDir "C:\Users\Me\Desktop\Macro Work"
' Variable Declarations
Dim EmptyRow As Long
Dim FileA, FileB As String
Dim Day as Integer
Day = 1
' Loop and combine the two files for each day of the month (if they exist)
Do Until (Day > 31)
' Get FileA and FileB names
FileA = "FileA" & Day & ".xls"
FileB = "FileB" & Day & ".xls"
' Open files A and B; handle error if file doesn't exist
On Error Resume Next
Workbooks.Open FileName:=FileA
If Err.Number = 1004 Then
GoTo NoFile
End If
Workbooks.Open FileName:=FileB
If Err.Number = 1004 Then
GoTo NoFile
End If
On Error GoTo 0
'Copy data from FileB and paste into FileA
EmptyRow = Workbooks(FileA).Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row + 1
Range(Range("A2"), Range("A2").End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy Workbooks(FileA).Worksheets(1).Range("A" & EmptyRow)
' Save combined workbook as FileName in Excel format
Workbooks(FileA).SaveAs FileName:="CombinedFile" & Day, FileFormat:=51
' Close the workbooks
Workbooks("CombinedFile" & Day).Close SaveChanges:=False
Workbooks(FileB).Close SaveChanges:=False
NoFile:
Day = Day + 1
Loop ' End of Loop
MsgBox "PAUSE - End of Macro"
End Sub
My problem isn't that that Macro won't run or throws an error. Rather if I run it and don't do anything else, I get one set of results. But if I run it and click on the VBA Editor Window while it's running, I get a different set of results.
Is it a known fact that you shouldn't do other things on your machine while your macro is running? Or have I neglected to add a piece of code that will keep the results of the macro consistent whether or not I sit and watch or am clicking the VBA Editor Window?
Thanks in advance, and let me know if I can provide further clarification.
Zachary
EDIT:
It's difficult to track down exactly what goes wrong with combining the files, but I can see that the number of lines differ. The combined file generated when I am clicking on the Editor Window has several thousand lines less than when I just watch it run. And I'll need to retract my earlier claim that I can consistently produce the same error. I can consistently reproduce an error, but the results may differ (i.e. maybe I'm missing only 4995 lines instead of 5000).
Liam, your response makes sense to me. However, I have a near identical Macro that skips this section:
'Copy data from FileB and paste into FileA
EmptyRow = Workbooks(FileA).Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row + 1
Range(Range("A2"), Range("A2").End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy Workbooks(FileA).Worksheets(1).Range("A" & EmptyRow)
Since sometimes the files I work with only have "FileA" I don't combine anything and instead use a Macro to open the file and SaveAs FileFormat=51 and with a new name. Obviously that removes sections in the code where I select data in the file. Yet I still receive different results when clicking. What might be causing this?
Lastly, in addition to removing sections of code where Selection is used, is there a way to help prevent the user from messing up the results of the macro in this way?
Thanks again.

its because you use:
Range(Range("A2"), Range("A2").End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Just because screenupdate is false, doesn't mean excel isn't executing your clicks. You can't reference a selection and expect it not to change if you select something else
Answers To Updated Question
You can easily remove the need to "select" cells by defining them in a range and referencing your new range. This will remove the possibility of users clicking and messing up the flow of your script
'Copy data from FileB and paste into FileA
Dim R1 as Range
Dim R2 as Range
EmptyRow = Workbooks(FileA).Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row + 1
Set R1 = Range(Range("A2"), Range("A2").End(xlDown))
Set R2 = Range(R1, R1.End(xlToRight))
R2.Copy Workbooks(FileA).Worksheets(1).Range("A" & EmptyRow)
This will do the same as your original code, without using select

One way to eliminate these problems is to reference the sheet in your calls (Sheets(1).Range(...)), and minimize selection (instead of .Select and then Selection., just put your Range.Copy or whatever you're doing). A little more code, but more robust.

Related

VBA Lookup in Another Workbook

I am having an vba problem with Excel. So I have this workbook, "Book Tool - Updated Feb. 2017.xlsb", that I am currently updating and will distribute to about 10 team members to use to keep track of their work. What I am trying to do is lookup data from an outside document, "Team Data", put that in Column DE of the "Book Tool - Updated Feb. 2017.xlsb" file. So I wrote the below code, where when the team member pushes a button, it opens up the lookup file, looks for the data in Column A of the "SICcode" sheet of that external file, matches it with Column B of the "Book Sheet" of the "Book Tool" file, and returns the value in Column D of the lookup file. It runs for the length of the "Book Sheet", closes the external file, and you get a popup that the data add is done.
Now when I did this code myself, it works great. Automatically opened the external document, did the lookups, returned the correct value, closes the external document, the pop up. So I sent the file with the macro to my manager to play around with before giving it to the rest of my team, but the macro does not work. When the macro runs, the external document opens, it seems like it is running through the lookups, closes the external file, and the pop up appears, but there is no value in the DE column, nor is there the lookup formula there. My manager didn't change the name of the Tool document, he didn't mess with any code. He emailed it back to me and with that copy the formula isn't working, but I checked it with my master copy formula and even though it's the same, the macro will not populate the data.
We have to keep the external data in a separate file, because otherwise the tool with the lookup data is over 2MB and takes forever to run or crashes.
Is there something about emailing the tool back and forth that messes with the file, or is there some formatting issue I need to look into that causes it not to work? With my master copy on my computer, the code always works regardless if I work in a virtual desktop, have it in a different folder, whatever.
I am just okay with vba, I don't know all of the technicalities of this process, so maybe I am overlooking some flaw with how I've set it up or limitations Excel has. Any guidance or help would be appreciated.
Sub AddData()
On Error Resume Next
'Open External Data Source
Workbooks.Open Filename:= _
"W:\USB\Reporting\Book Tool\Attachments\Team Data.xls"
'View sheet where data will go into
Windows("Book Tool - Updated Feb. 2017.xlsb").Activate
'Gets last row of Tool sheet
Sheets("Book").Select
lastrow = Cells(Rows.Count, "B").End(xlUp).Row
'Lookup in External File
Sheets("Book").Select
Range("DE2:DE" & lastrow).FormulaR1C1 = "=VLOOKUP(RC[-108],'[Team Data.xls]SICcode'!C[-109]:C[-104],5,FALSE)"
'Close External Data File
Windows("Team Data.xls").Activate
ThisWorkbook.Saved = True
Application.DisplayAlerts = False
ActiveWindow.Close
MsgBox "Data Add Done"
End Sub
Be sure to properly qualify your statements, and also it would be wise to assign the appropriate workbook to a variable. See the modified code below:
Sub AddData()
On Error Resume Next ' I also suggest removing this since it wont warn you on an error.
Dim wb as Workbook
Dim wbExternal as Workbook
Dim ws as Worksheet
Dim wsExternal as Worksheet
'Open External Data Source
Set wbExternal = Workbooks.Open Filename:= _
"W:\USB\Reporting\Book Tool\Attachments\Team Data.xls"
' Depending on the location of your file, you may run into issues with workbook.Open
' If this does become an issue, I tend to use Workbook.FollowHyperlink()
'View sheet where data will go into
' Windows("Book Tool - Updated Feb. 2017.xlsb").Activate
' Set wb = ActiveWorkbook
' As noted by Shai Rado, do this instead:
Se wb = Workbooks("Book Tool - Updated Feb. 2017.xlsb")
' Or if the workbook running the code is book tool
' Set wb = ThisWorkbook
'Gets last row of Tool sheet
Set ws = wb.Sheets("Book")
lastrow = ws.Cells(ws.Rows.Count, "B").End(xlUp).Row
'Lookup in External File
Set wsExternal = wbExternal.Sheets("Book")
wsExternal.Range("DE2:DE" & lastrow).FormulaR1C1 = "=VLOOKUP(RC[-108],'[Team Data.xls]SICcode'!C[-109]:C[-104],5,FALSE)"
'Close External Data File
ThisWorkbook.Saved = True
Application.DisplayAlerts = False
Windows("Team Data.xls").Close
MsgBox "Data Add Done"
End Sub
I would also recommend browsing through SO for tips on avoiding .Select and .Activate as this can make your code unreliable and in some cases can slow down your code significantly.
Lastly, if performance is a concern you may want to look into loading your lookup values into arrays and finding the corresponding values this way. It will completely depend on what kind of data you are working with. I had a workbook using filldown vlookups that went from running in a matter of 5-10 minutes or more to consistently running in less than 20 seconds by replacing VLOOKUPS with for looping arrays.

Copy/Paste variable dataset between workbooks without clipboard

I've been trying to optimize some of my coding and managed to cut and speed it up a lot. However there are some things that are still quite clunky (and me still a noob). Backstory is that my code is opening source and target files, copies a lot of data of variable length, closes source and then does a lot of operations and finally saves target file.
One of the things Id like is to do if possible is a direct copy of data without using clipboard, activating workbooks, selecting sheets (whatever of this is possible to pack into more efficient code that I am currently having)
Windows("SOURCE.xlsm").Activate
Sheets("Data").Select
Range("A2:AX10").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Windows("TARGET.xlsm").Activate
Range("A2").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Is it possible to do a selection (A2:AX10 and all the way down to last row) in SOURCE file-Data sheet and directly copy it to TARGET file-Data sheet cell A2 without using clipboard.
The reason why I use A2:AX10 and then selection down is because I have a lot of blank cells in the whole data set and this way I get entire data.
I would like to be able to to that selection and use it as a range in this line
Workbooks(“SOURCE”).Worksheets("Data").Range(“A2:AX10 & ALLTHEWAYDOWN”).Copy _Workbooks(“TARGET”).Worksheets("Data").Range(“A2")
I was trying to solve this but I dont end up with desired result. When I try doing selection and setting as range then both trying copy range with activitng workbooks and in the direct copy mode I get 1004 error.
Is it possible to optimize this chunk and make it work. It would improve a lot of my VBA.
Thanks,
You need something like this:
With Workbooks("SOURCE.xlsm").Sheets("Data")
.Range("A2:AX10", .Range("A2:AX10").End(xlDown)).Copy Workbooks("TARGET.xlsm").ActiveSheet.Range("A2")
End With
You could probably also use CurrentRegion rather than End(xlDown
You can set one range's values (the range where you would want to paste values) equal to a source range's values (the range which you would previously have copied).
Sub paste_values()
Dim wb_A As Workbook, ws_A As Worksheet
Dim wb_B As Workbook, ws_B As Worksheet
Dim last_row As Long
Set wb_A = ThisWorkbook
Set ws_A = wb_A.Sheets(1)
Set wb_B = Workbooks("WorkbookB")
Set ws_B = wb_B.Sheets(1)
With ws_A
last_row = .Range("A" & .Rows.Count).End(xlUp).Row
End With
ws_B.Range("A2:AX" & last_row).Value = ws_A.Range("A2:AX" & last_row).Value
End Sub
This code is setting the new range's values equal to the original range. It prevents the need to activate sheets or workbooks, whilst also copying data to a range without filling the clipboard.
I would also recommend using last_row = .Range("A" & .Rows.Count).End(xlUp).Row to find the last row of your data. Although you do need to ensure you use this on a column which you know contains continuous data.

Run Time error 1004: Select Method of Worksheet class failed with (varCellContent.Parent.Name)

The code edits, then copies data from a temporary sheet to the current workbook. I am using varCellContent to ask the user to choose the source sheet. I had no trouble when testing the code as I was building it by using F8 to step through (and the code still works perfectly like this).
But when I run the macro normally, I am getting
Run Time error 1004: Select Method of Worksheet class failed
on the following line:
Workbooks(varCellContent.Parent.Name).Worksheets("Sheet1").Select
The following may not be relevant to the cause of the problem but I have read a bit about it while searching for an answer.
It sounds like using .Select is not a good idea, but there are 2 reasons I thought it was useful in this situation.
I don't have to name the sheet for each of the following rows of code that delete and move the columns.
When building and debugging the code I wanted the sheet to be visible so that I could see what was happening as I stepped through each line of code.
So taking those 2 reasons into account is there still a better way to avoid using .Select?
Sub ImportNewData()
'
'
' Import data, save as new workbook
Dim LR As Long
LR = Range("A" & Rows.Count).End(xlUp).Row
' Clear data
Range("A3:I" & LR).ClearContents
' User choose source sheet
Dim varCellContent As Worksheet
Set varCellContent = Application.InputBox _
(prompt:=" " & vbNewLine & "Click any cell in the Aged AP Summary that has been exported from Systematic to Excel, then click OK", Type:=8).Parent
Workbooks(varCellContent.Parent.Name).Worksheets("Sheet1").Select
'Delete unrequired columns
Columns("E:I").Select
Selection.Delete Shift:=xlToLeft
Worksheets("Sheet1").Activate
Columns("E:I").Delete Shift:=xlToLeft

VBA Import Data

I have a problem and I think it's complicated. It's regarding importing data/information from a another Excel file and the data/information needed is on 2 sheets.
The code is working but not the way It should be it doesn't seem to import the data and down below it will explain what we cannot do in it, ("me and my friend that are working in this project that was demand by our company don't know almost nothing about this category VBA and we are just researching info about this but get always stuck").
We need to extract from the file (the link is in the description), and the file is created every week(semana) so somehow it needs to extract the newest every week.
Down below there's the Excel file that is responsible for the extraction of the data for security reason we deleted most of the information on it and changed names.
Workbook with the Macro - Used for importing The sheet is the number 2 and also It's called Dev.Pag the macro is associated to the button "IMPORT DATA" in the same file.
Source Workbook - Contains the data This is where it gets the values(exports) from once again the name and some data was erased because It might compromise the company.
If there's anything I can edited or change please tell me. Thanks in advance for any reply.
Also will post the code down below:
Option Explicit
Sub ImportData()
Application.ScreenUpdating = False
Dim Path As String, Lstrw As Long
Dim SourceWb As Workbook
Dim TargetWb As Workbook
'Change this to your company workbook path
Path = "C:\Users\DZPH8SH\Desktop\Status 496 800 semana 12 2015.xls"
Workbooks.Open (Path)
'Change "Source" to the name of your company workbook
Set SourceWb = Workbooks("Status 496 800 semana 12 2015.xls")
'Part that needs some adjustments in down below
'This part is working good but probably some error in the data
'transferance may be intrefering with the integridty
'change the file address
Set TargetWb = Workbooks("Master_Atual_2015.xlsm")
Lstrw = SourceWb.Sheets(1).Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlValues).Row
With SourceWb.Sheets(1)
.Application.Union(.Range("D2:D" & Lstrw), .Range("F2:F" & _
Lstrw), .Range("I2:I" & Lstrw), .Range("M2:M" & Lstrw)).Copy _
Destination:=TargetWb.Sheets(1).Range("A3")
End With
SourceWb.Close savechanges:=False
Application.ScreenUpdating = True
End Sub
It was pretty hard to understand your code for several reasons:
You just reference the sheets by index. Thus they are hard to find. Better reference them by name
you reference ranges only by addresses, it would be better to define named ranges
Another point to make it more readable/ debuggable, set the union to an own range object to be able to watch the content and be sure that this is what you want to have.
With SourceWb.Sheets(1)
Set objTargetRange = .Application.Union(.Range("D2:D" & Lstrw), .Range("F2:F" & Lstrw), .Range("I2:I" & Lstrw), .Range("M2:M" & Lstrw))
objTargetRange.Copy Destination:=TargetWb.Sheets(1).Range("A3")
End With
When printing out the objTargetRange's addresses it looks like this (You can do that by simply pressing ctrl+g and writing ?objTargetRange.Address)
$D$2:$D$9;$F$2:$F$9;$I$2:$I$9;$M$2:$M$9
So you selected four columns, each of them has 8 cells. When adding this to A3, it will be added side by side, it doesn't have any offset.
So cells A3-D10 will be overwritten by your data, the code works. It was not visible that it does that, because comparing state before and state after was pretty complicated. You can make visible, that the changes actually happen by just
temporary removing your actual data from the sheet
temporary adding some empty rows

Copy data from multiple excel sheets and append that to a single excel sheet using VBScript

The scenario is as follows:
I have an excel (.xls) file with data. (eg. A.xls)
The Data on this excel file are on a single worksheet (Sheet 1).
The number of columns in this file is fixed i.e. 8
However, the number of rows containing data may vary from time to time. (This file is updated by another program from time to time)
Now, I have another excel file (eg. B.xls) with similar type of data but not same as the contents of A.xls.
The number of columns in B.xls is 8 as well. However, the number of rows containing data are unknown.
I want to copy the contents of A.xls, 2nd row onwards (excluding the 1st row containing the column headers) and append/paste the same to the B.xls file, without over-writing the existing data on B.xls.
With all these details in mind, I want to write a vbscript to automate this task.
Please help.
Thanks a lot, in advance.
It needs a lot of cleanup, but something like this should work. I'll clean it up a bit and then make an edit.
Sub CopyRows()
' Choose the name of the Second Workbook and last column.
' It must be in the same directory as your First Workbook.
secondWorkbook = "B.xls"
lastColumn = "H"
' A couple more variables
currentWorkbook = ThisWorkbook.Name
Workbooks.Open ThisWorkbook.Path & "\" & secondWorkbook
' In the First Workbook, find and select the first empty
' cell in column A on the first Worksheet.
Windows(currentWorkbook).Activate
With Worksheets(1).Columns("A:A")
Set c = .Find("", LookIn:=xlValues)
If Not c Is Nothing Then
' Select and copy from A2 to the end.
secondAddress = Replace(c.Address, "$A$", "")
Range("A2:" & lastColumn & CStr(CInt(secondAddress) - 1)).Select
Selection.Copy
End If
End With
' Activate the Second Workbook
Windows(secondWorkbook).Activate
With Worksheets(1).Columns("A:A")
Set c = .Find("", LookIn:=xlValues)
If Not c Is Nothing Then
' Select and paste the data from First Workbook
Range(c.Address).Select
ActiveSheet.Paste
End If
End With
End Sub
Update: That should do the trick. I copied from the wrong workbook the first time around, too. Let me know if you have questions.
This is something the Macro Recoder could have written for you. You would come out with different approach.
Turn on recording. Open A.xls and B.xls. Move down one row on a. Press Shift+End then →, then Shift+End+↓. Then Ctrl+C to copy your data. Switch back to B. End+↓, ↓. Ctrl+V to paste. Turn off recording.
You can record in Excel.
Alt+T,M,R
then Home key then ↑. Stop recording.
Look what Excel wrote
Selection.End(xlUp).Select
or if you had of recorded Go To dialog
Application.Goto Reference:="R1C1"
or if you had of recorded Ctrl+Home
Range("A1").Select
To convert to vbscript
Record the steps in excel macro recorder. You have to rewrite it a bit because it uses a type of syntax that vbs doesn't.
This applies (I don't have a medium9) xlRangeAutoFormatAccounting4 in vba.
Selection.AutoFormat Format:=xlRangeAutoFormatAccounting4, Number:=True, _
Font:=True, Alignment:=True, Border:=True, Pattern:=True, Width:=True
So first look up constants in vba's object browser. xlRangeAutoFormatAccounting4 = 17
Then look the function up in object browser and look at the bottom for the function definition,.
Function AutoFormat([Format As XlRangeAutoFormat = xlRangeAutoFormatClassic1], [Number], [Font], [Alignment], [Border], [Pattern], [Width])
So the vba becomes in vbs (and vbs works in vba) (and as you can see you can work out the correct way without needing to look the function up usually)
Selection.AutoFormat 17, True, True, True,True, True, True
So your code becomes
objXLWs.Range("A3").CurrentRegion.Select.AutoFormat 17, True, True, True,True, True, True