VBA Macro running too fast - vba

It's weird that I'm finding ways to slow down my macro. Apart from Doevents and other time delay techniques, which are basically a workaround, is there a way through which we can get around the asynchronous execution. As in, I want the VBA code to behave like this:
start executing line 1>finish executing line 1>move to line 2;
Forgive if I'm wrong but currently it seems to follow:
Start executing line 1>without caring whether line 1 finished or not start executing line2

If you are calling external programs (vbs, exe) then the vba isn't getting any feedback on the process at all. It calls the programs and moves on to the next line of code (the vba doesn't know if/when the external programs finishes).
One way to slow this process down would be to put a application.wait or application.sleep between the calls, but that is also a workaround. Please post your actual code and perhaps we can troubleshoot further.

if the code is about refreshing data, use Refresh method with backgroundquery=False in a For Loop instead of RefreshAll.
For Each con In Me.Connections
con.ODBCConnection.BackgroundQuery = False
con.Refresh
Next

Related

How can I step into a line that is normally not called?

Sometimes, I want to manually put the execution point to a certain line.
Because I don't want it to normally happen, but only when I put the execution point to this line manually, I name it this way:
If 0 Then
_SomeValue = "This can pnly be hit by setting the execution here manually"
End If
In VB6, it worked. It was one of my favorite ways to do some things.
In VB.NET, I am unable to do put the executor to this line.
Each time I do, it is set to the next line.
Is there any way to do what I want?
I got the suggestion to try If False Then, but it doesn't work for me:

VBA execution flow interrupted unexpectedly

Context of the problem
I'm developing a new feature for an HMI using Factory Talk View Studio 7.00.00 (CPR 9 SR 6) and VBA 6.5.
I have two displays: ma1_header and ma2_header and, as many of you know, in Factory Talk View Studio (I'm going to call it as FTV for brevity from now on) each display has a dedicated DisplayCode.
A Display code can be seen as the VBA code behind an Excel file that stays open as long as its excel file. From this point of view, the VBA code bounded to a display in FTV has the same behaviour of a VBA project in excel, so it's closed when the graphic display it's closed by the user or from code.
Another important point in order to understand the problem is that when a generic display in FTV called A is opened in Replace mode with at least one pixel that overlaps another display B, the display B is closed with its VBA code. Take in mind that ma1_header and ma2_header are always opened in Replace mode.
Problem description
That said, now I'm going to describe the problem that I found.
The VBA code bounded to ma1_header and ma2_header is mostly the same (the differences are pointed out in the following schema) and it performs some init actions on display start and after those it runs a procedure called ScheduleCheck. This procedure updates some UI components and it evaluates some conditions in order to determine if it's time to show ma2_header (ma1_header if the code it's executed behind ma2_header), then it recalls itself.
The command that opens a display is not executed directly from VBA, its executed asynchronously outside VBA. In fact VBA for some actions like: "Show a display", "Set tags values", etc. can uses a library that enables it to tell to a FTV service (that also can be used with a command line tool) to perform a list of these actions (1 command or more transmitted at time).
When a user starts a FTV client the ma1_header is shown first with some others displays which compose the user interface.
In ma1_header, when the opening conditions for ma2_header are satisfied and it's opened, the problem comes out. Let's proceed now with a step by step description of the VBA state, in order to be as clear as possible on the problem:
In ma1_header the opening conditions are satisfied and the asynchronous command that shows ma2_header is excuted. Note that for the moment ma1_header vba is still open.
When ma2_header start opening the code it's executed without any problem till the wait procedure. The wait procedure is written as follow:
Public Declare Function GetTickCount Lib "kernel32" () As Long
Public Sub wait(lMillSec As Long)
Dim lT2 As Long
Dim lT1 As Long
On Error GoTo errHandle
Const strMethod = "wait"
MsgBox "wait - 1"
lT1 = GetTickCount
lT2 = lT1
While lT2 - lT1 < lMillSec
lT2 = GetTickCount
DoEvents
Wend
errHandle:
If Err.Number Then
LogDiagnosticsMessage "VBA: Display " & Me.Name & " in Method " & strMethod
Err.Clear
End If
End Sub
The execution of the wait procedure proceed without any problems until the DoEvents command on the 18th rows. When DoEvents it's executed the ma1_header has the time to close itself definitively (even its vba code) then the vba flow in ma2_header seems to stops here without any error. Due to this the ScheduleCheck can't recall itself.
Ugly solution 1
Currently I found what I call an ugly solution that let code works.
When ma1_header is open, the ma2_header opening will force the close of ma1_header that , as I explained before, is completed only when DoEvents is executed.
I tried to transmit this new list of commands to the FTW tool when the ma2_header need to be shown:
##New##
Abort MA2_HEADER;
Pause 1;
Display MA1_HEADER /TRRU;
##Old##
Display MA1_HEADER /TRRU;
With the new approach I close the ma2_header, then I wait (in the FTV tool, not in VBA) for 1 second with the command "Pause 1;" then I open ma1_header when I'm reasonable sure that ma1_heder is closed thanks to the pause command.
In this way the ma2_heder periodically executes the procedure ScheduleCheck without strange execution interruption.
I don't know why this solves my problem, so I'd like to understand why it works and which is the cause of this problem in order to find a better solution.
Ugly solution 2
I found another ugly solution, but as before I'm not satisfied because I would like to know way my current code has this problem (for me solving a problem without knowing what is the cause it's a defeat as a programmer).
I've created the following new tag on the Tag server of FTV
I've created the following new event on FTV:
I've added a string display containing the new tag Tick in ma1_header and in ma2_header
Now, in VBA, I can use the Change event of this string display in order to execute the same code contained in ScheduleCheck (obviously without the wait with DoEvents loop) each time this string display changes (each second).
Any clarification on the problem or a better solution would be really appreciated.

.NET Equivalent for VB6 DoEvents + Sleep

I have a program that I'm updating from VB6 to VB.NET that is used for making optical measurements. The hardware runs Win XP or Win 2000 Embedded. In the original code, there is a section that is triggered after a measurement is started that uses DoEvents and Sleep:
While instDefItf.InstrumentState = Busy
Sleep 500
DoEvents
Wend
I've tried replacing the middle two lines in my code with a simple Threading.Thread.Sleep(500) (I realize that this is probably poor practice, but not having a ton of experience with VB, I was trying to preserve the VB6 program as closely as possible). This makes the code work correctly - on the second try. On the first try, the measurements that are supposed to be taken simply aren't, but no errors are thrown and the subsequent code executes correctly.
Based on this, I have two primary questions. First, is there something I could simply replace the VB6 code with to get the desired functionality? Second, is there a better way to periodically query the instrument state that doesn't make use of the Sleep function?
EDIT:
The function I referred to above is a helper function (I think that's the right term) that checks to see if the instrument has completed its measurement. The measurement routine is contained within a DLL, and the code that executes it is given below.
If cbMeasLength.Value = vbUnchecked Then
Call I12001Ref.StartMtj1IlAcquisition2(CLng(lChannel), ConnectorA, m_moduleCollection.ReflectanceModule(lRm).SerialNumber)
End If
' Wait the measurement completion
If cbMeasLength.Value = vbUnchecked Then
If WaitForOperationCompleted = False Then
Exit Sub
End If
End If

Running a loop while debugging VBA

The Problem
I am trying to debug some code, and somewhere in the middle I stopped at a breakpoint. Now I want to change some variables and run a certain loop several times.
How far did I get?
I know how to change the variables, but somehow I get stuck when trying to run the loop in the immediate window. Here is an example:
Dim i As Integer
Dim j As Integer
For i = 0 To 6
j=i ' Do something
Next i
I tried several variations of the code, but each time I get the following error:
Compile error: Next without for
Other relevant information
I tried searching but mostly found information about problems with loops, whilst I am quite sure the loop itself is fine. (Especially as I reached it before arriving at the breakpoint).
The only place I saw someone addres this situation, he reduced the loop to a single line, however to do this every time would be very impractical in my case.
I realize that I could call a function containing the loop, and then the function call would probably work, but again this feels quite impractical. So I guess it boils down to the following question.
The question
What is a practical way to run a loop whilst debugging VBA code in Excel?
There is actually a way for using loops or other multi-line statements in the Immediate Window - using a colon : to separate statements instead of a new line.
Full solution is described here.
Note that in the Immediate Window you also don't have to declare the variables using a Dim statement.
To summarize, your snippet would look something like this:
For i = 0 To 6: j=i: debug.Print i+j: Next i
I think I understand your question. You want to run a multi-line code block (i.e. the loop) in the Immediate Window. This throws errors because the Immediate Window is only intended for single lines of code.
I don't have any suggestions other than those you already mentioned. I'd recommend putting your test loop into a separate function and calling that from the Immediate Window:
Sub Test()
Dim i As Integer
Dim j As Integer
For i = 0 To 6
j=i ' Do something
Next i
End
Another option is to set several breakpoints. You can also run one line of code at a time with F8.
What is likely the preferred method (i.e., what most people actually do) is use the full power of the IDE, which includes the Immediate, Locals and Watch panes. You can change the value of most variables at runtime by direct assignment in the Immediate Pane (i=6 will do exactly what you think it should do). The IDE also allows you to set breakpoints, add watch conditions, step through code line-by-line using the F8, step through function or procedure calls using Shift+F8, stepping over (and back) through code using the mouse/cursor, and with a few exceptions, you can even add new variables during runtime.

Strange / unstable behaviour on CreatePivotTable()

I am writing VBA code to automate some processes in Excel and I am encountering a very strange behavior for which I have not been able to find documentation / help.
I have a procedure MAJ_GF that first executes function GF.Update, checks the result, and then launches procedure GF.Build (which basically takes the data obtained by GF.Update from different worksheets and does a bunch of stuff with it).
At some point, this "bunch of stuff" requires using a pivot table, so GF.Build contains the following line:
Set pvt = ThisWorkbook.PivotCaches.Create(xlDatabase, _
"'source_GF'!R1C1:R" & j & "C" & k).CreatePivotTable("'TCD_GF'!R4C1", "GFTCD1")
The strange behavior is this:
when I run MAJ_GF, VBA properly executes GF.Update, then launches GF.Build, and stops at the line described above complaining "Bad argument or procedure call"
when I manually run GF.Update, then manually run GF.Build, everything goes smoothly and GF.Build does what it has to do from beginning to end with no errors
even stranger, when I set a break-point on the incriminated line, run MAJ_GF then VBA pauses on the line as expected, and when I say "Continue"... it just continues smoothly and with no errors !
I turned this around and around and around, double-checked the value of every variable, and this just makes no sense.
Ideas anybody?
Few ideas come to my mind:
There's still some update going on in the background. Try DoEvents and Application.Wait before the line you mentiond
Also check, if any data connections are able to update in the background - if so disable the background refresh
Very rarely (usually in older version and when involving Charts), unhiding the Excel window (in case you used Application.Visible = False and enabling ScreenUpdating helped..
Are you using any "exotic" references/add-ins? Disable them and see if the problem persists.
Try restarting your machine
Not that I'm too optimistic that either will solve your problem - but give it a try! Best of luck!