View Full Version : A way to speed up code VBA in for loop with ExecuteExcel4Macro
rong3
10-31-2017, 12:17 AM
Hi there! , as theard title the time of code excute takes about 1 min on normal PC and take much than on other lower PC.
By using  ExecuteExcel4Macro to read value of closed file and For loop to get row by row value of it as below code:
For r = 1 To 500 ' 500 sample data
     'Condition for ending Sub if the row at E is empty
     If Replace(CStr(ExecuteExcel4Macro(Ret & Range("E" & r).address(, , xlR1C1))), " ", "") <> "0" Then
      If UCase(CStr(ExecuteExcel4Macro("'" & wbPath & "[" & wbName & "]" & title_trakhuon & "'!" & Range("H" & r).address(, , xlR1C1)))) = "V" _
      And CDate(CLng(ExecuteExcel4Macro(Ret & Range("J" & r).address(, , xlR1C1)))) = CDate(UserForm1.txt_ngay.Text) _
      Then
      count_dap = count_dap + 1
       For chay = 0 To 6 'get each column (6 cols)
           UserForm1.txt_editor.Text = UserForm1.txt_editor.Text & ExecuteExcel4Macro("'" & wbPath & "[" & wbName & "]" & title_trakhuon & "'!" & Range("A" & r).Offset(0, chay).address(, , xlR1C1)) & "  |  "
        Next chay
           UserForm1.txt_editor.Text = UserForm1.txt_editor.Text & vbNewLine
           UserForm1.txt_editor.Text = UserForm1.txt_editor.Text & "-------------------------------------------------------------------------------------------------------------------------------" & vbNewLine
       End If
       Else:
        UserForm1.lbl_count_dap = "Status: There counts " & count_dap & " data(s)"
       UserForm1.Show
       Exit Sub
       End If
    Next r
The for looping get each rows of closed file by ExecuteExcel4Macro  then set it into txt_editor which is textbox.
Is there other way to optimize code , it takes too much time. Thanks for reading, hope the helps of all.
Jan Karel Pieterse
10-31-2017, 06:18 AM
Have you timed whether just opening the file in question and getting the values from the cells isn't actually faster?
rong3
10-31-2017, 05:51 PM
Have you timed whether just opening the file in question and getting the values from the cells isn't actually faster?
It is totally slow, all the time it got value into textbox takes about 1min30s
Kenneth Hobs
10-31-2017, 06:04 PM
It takes long to open by Workbooks.Open method or using excel4 method?
The excel4 method opens and closes the file on each call. The method can get more than one cell value in one call.  It is better to put the results into array(s) and then work with them.
String concatenation takes a long time if you build large strings using & method.
Say in words what you want to do.  Show data with sample file(s) attached.
Paul_Hossler
10-31-2017, 06:12 PM
If you're looking to retrieve  500 (+/-) cells from a closed workbook, it seems like it'd be faster to open the workbook in the macro, grab the data, close the workbook, and then build your string
rong3
10-31-2017, 06:25 PM
It takes long to open by Workbooks.Open method or using excel4 method?
The excel4 method opens and closes the file on each call. The method can get more than one cell value in one call.  It is better to put the results into array(s) and then work with them.
String concatenation takes a long time if you build large strings using & method.
Say in words what you want to do.  Show data with sample file(s) attached.
Takes long by using excel4 method. I have no choice in using concatenation to get value, but it takes about 15 length of value at all.
If put the results into array(s), so then i must put array results into textbox which I want to show and i think the time takes longer.
All I want to do is speed up as title by getting value from closed file and put it into textbox to show.
rong3
10-31-2017, 08:04 PM
If you're looking to retrieve  500 (+/-) cells from a closed workbook, it seems like it'd be faster to open the workbook in the macro, grab the data, close the workbook, and then build your string
Yah, it takes only 2-3 seconds in speed, but I must open in background then close it, but I tried code about close background file , seems not close.
Kenneth Hobs
10-31-2017, 09:24 PM
My tests found this:
3,000 GetValue() calls = 47.734375 seconds 
1 GetObject() call = 0.5625 seconds
I used array methods.  For the GetObject() method. A Copy GetClipboard method might be a bit faster.
Create 4 textbox controls with the first two with MultiLine set to True. Add one CommandButton.  Put single textbox3 below the big textbox1.  
Private Sub UserForm_Initialize()
  Dim d As Double
  
  d = Timer
  Test1
  '3,000 calls=47.734375 seconds
  TextBox3.Value = Timer - d & " seconds"
  
  d = Timer
  Test2
  '1 call=0.5625 seconds
  TextBox4.Value = Timer - d & " seconds"
End Sub
Private Sub Test1()
  Dim calc As Integer, p$, f$, r$, s$, t$
  Dim i As Long, j As Integer, a, rr As Range
  
  p = ThisWorkbook.path '& "\"
  f = "Rows Columns.xlsx"
  s = "Sheet1"
  r = "A1:F500"
  On Error GoTo EndSub
  Application.ScreenUpdating = False
  Application.EnableEvents = False
  calc = Application.Calculation
  Application.Calculation = xlCalculationManual
  
  Set rr = Range(r)
  ReDim a(1 To rr.Rows.Count, 1 To rr.Columns.Count)
  For i = 1 To rr.Rows.Count
    For j = 1 To rr.Columns.Count
      a(i, j) = GetValue(p, f, s, Cells(i, j).Address)
    Next j
  Next i
  TextBox1.Value = Join2D(a)
  
EndSub:
  Application.ScreenUpdating = True
  Application.EnableEvents = True
  Application.Calculation = calc
End Sub
Private Sub Test2()
  Dim calc As Integer, p$, f$, r$, s$, t$, fn$
  Dim i As Long, j As Integer, a, rr As Range
  
  p = ThisWorkbook.path & "\"
  f = "Rows Columns.xlsx"
  s = "Sheet1"
  r = "A1:F500"
  fn = p & f
  
  If Dir(fn) = "" Then Exit Sub
  On Error GoTo EndSub
  Application.ScreenUpdating = False
  Application.EnableEvents = False
  calc = Application.Calculation
  Application.Calculation = xlCalculationManual
  
  a = GetObject(fn).Worksheets(s).Range(r).Value
  Workbooks(f).Close False
  TextBox2.Value = Join2D(a)
  
EndSub:
  Application.ScreenUpdating = True
  Application.EnableEvents = True
  Application.Calculation = calc
End Sub
Private Sub CommandButton1_Click()
  Unload Me
End Sub
'Retrieves a value from a closed workbook
Private Function GetValue(path, file, sheet, ref)
  Dim arg As String
  'path = "d:\files"
  'file = "budget.xls"
  'sheet = "Sheet1"
  'ref = "A1:R30"
     
  'Make sure the file exists
  If Right(path, 1) <> "\" Then path = path & "\"
  If Dir(path & file) = "" Then
    GetValue = CVErr(xlErrNA)
  End If
     
  'Create the argument
  arg = "'" & path & "[" & file & "]" & sheet & "'!" & _
  Range(ref).Range("A1").Address(, , xlR1C1)
   
  'Execute an XLM macro
  GetValue = ExecuteExcel4Macro(arg)
End Function
'http://dailydoseofexcel.com/archives/2015/01/28/joining-two-dimensional-arrays/
Public Function Join2D(ByVal vArray As Variant, _
  Optional ByVal sWordDelim As String = vbTab, _
  Optional ByVal sLineDelim As String = vbCrLf) As String
  
  Dim i As Long, j As Long
  Dim aReturn() As String
  Dim aLine() As String
  
  ReDim aReturn(LBound(vArray, 1) To UBound(vArray, 1))
  ReDim aLine(LBound(vArray, 2) To UBound(vArray, 2))
  
  For i = LBound(vArray, 1) To UBound(vArray, 1)
    For j = LBound(vArray, 2) To UBound(vArray, 2)
      'Put the current line into a 1d array
      aLine(j) = vArray(i, j)
    Next j
    'Join the current line into a 1d array
    aReturn(i) = Join(aLine, sWordDelim)
  Next i
  
  Join2D = Join(aReturn, sLineDelim)
End Function
rong3
11-01-2017, 02:55 AM
Thank you so much, I better chose open closed file in background and pass all to array and dispose it. It now takes 5-6 seconds at all. Thanks for supporting me!
The simplest/fastest method:
Private Sub Userform_initialize()
  With getobject("G:\OF\example.xlsx")
    .sheets(1).range("A1:F50").copy
    With GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
      .GetFromClipboard
      TextBox1.Text= .GetText
    End With
    .close 0
  end with
End Sub
Kenneth Hobs
11-01-2017, 07:27 AM
I guess the "might" in post #8 was wrong.  Even so, it is the more simple method as snb said. In speed tests, when I see numbers less than one second, I am happy with that.
In these tests, Test3 is the clipboard method as snb showed in post #10.  I just added Application.DisplayAlerts=False as large blocks of data will throw up the save clipboard prompt.
When I test speed, what runs first can make a big difference.  Usually, it is the 1st run of any macro that is the slowest. Speed is also affected by running in VBE vs. other methods. These tests were ran in the VBE.
Test3          0.31640625 
Test2          0.19921875 
Test3          0.3125 
Test2          0.19921875 
Test2          0.22265625 
Test3          0.2890625 
Test2          0.22265625 
Test3          0.2890625
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.