How do I yield to the message pump in Excel VBA 2010? - vba

I have an Excel macro that takes an exceedingly long time. It seems to crash Excel (I get a Not Responding message in the taskbar) when I leave it unattended, but if I put a breakpoint that gets hit every five or ten seconds, it's OK. Trouble is, I'll need to step on from the breakpoint about a quarter of a million times (we're looking at about 200 hours to execute this beast)
I'm guessing that the absence of message pump activity is antagonising the kernel, so how might I flush the message queue? Or am I barking up the wrong tree entirely?
I'm very new to this, by the way, so documentation references will be very welcome.
Cheers,
Guy

Seconding the opinion that 200h is a bit worrying, however you can try the DoEvents statement which "surrenders execution of the macro so that the operating system can process other events". (Note that calling DoEvents has an additional time penalty)

I have to agree with 0xA3, if you have an excel routine that is expected to take over 200 hours to run then you're either setting it up in a very inefficient manner or you're using the wrong tool for this job.
In any case, there are a few things you can do to optimize the routine. These will be especially helpful if you are constantly writing values to individual cells.
Sub MarathonCalc
Application.EnableEvents = False
Application.ScreenUpdating = False
' Code to decode human genome goes here
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Again, if you post some of your code perhaps we can help track down why it's taking so long.

"Not responding" doesn't mean that Excel is crashing, it simply means that it does not actually respond to window messages.
Unless that is a problem there is nothing you need to worry about. I'm rather worried about your comment that you expect the task to take 200h (which is more than a week!). It seems to me that you are not using the right tool for this job. Try to find out what is taking so long and explain what you are actually doing. Then we can see how to better solve this task.

The previous answers have given you 'DoEvents' and that's the first-line fix for your problem.
However... A closer look at what's happening raises some worrying questions. Firstly, Excel's threading model is rather primitive: there's one process (according to the documentation) and no new threads are created by internally- or externally-generated events, callbacks and messages. One process, and one process ONLY, and everything else has to wait until the current VBA macro or spreadsheet calculation runs to completion.
Actually, some things can interrupt this: {esc} and {ctrl-break} (although these can be temporarily disabled), so there's at least one other thread running in Excel all the time, and you'd EXPECT the operating system to be allowed to break into a process... Wouldn't you?
The ugly reality is that the Excel application does not allow external events and messages to run to an 'event sink' while a macro or calculation is running, unless you call 'DoEvents' and some Windows Messages (note the capitalisation) still won't be dealt with.
If an undocumented number of messages and external events are left hanging around, the application will crash. Based on my own tests, I think the number is 50; there's documentation about a registry setting out there, but I'm not linking to material that I consider unreliable - I've tried the setting in question and found it to be ineffective.
So where does that leave you?
Firstly, look at your code again. Application.EnableEvents = False and Application.ScreenUpdating = False are useful settings: your next step is to find all and every read- or write operation to individual cells on the sheet and just grab arrays of cells - it's much faster to grab an array using myVBAarray=Range.Value, do your calculations in VBA, and write the VBA array variant back to Range.Value.
Secondly... You've also seen that the 'wait' state imposed by a breakpoint allows some things to sort themselves out. That sounds like an external process (a database query?) running asynchronously: could it be that the message or event which is crashing Excel is coming from that very process?

Related

Causes for random break points unknown

This has been a long and unresolved problem with Excel VBA code that runs a repeated loop via the Application.OnTime Now + TimeSerial(x,x,x). Users will find that their code is running fine for a few days and may, or may not, mysteriously stop.
I'm facing such a situation and hoping to resolve it. I've read over 30 forum answers, some written by experienced developers, and came to this understanding. My conclusion and question follow.
The conclusion is that Excel randomly goes into a break mode but no one knows the reason why this occurs, more so, why it randomly occurs.
Shall we then conclude that in fact this randomly occurs? And that Excel VBA isn't as robust as other languages.
Some notes:
I know this can be resolved by pressing Ctrl+Break twice. It doesn't explain why we have to do it in the first place.
I realize that this error is code independent. It'll occur with both simple and involved programs.
After trying multiple ways to simulate this error, and I mean a lot - long and multiple ADODB SQL connections and queries, cell editing while macro is running, using multiple and 1 second Application.OnTime recursive calling, I can't replicate the error. It is truly random.
I'm running only one workbook and one Excel instance.
Some say, for each break point we did in the debugging, that break point remains in memory. And then when we run a macro in the future, a write to that part of memory triggers this random break. This is a plausible explanation and does conclude that this error is random. No way can we inspect memory in VBA.
I need something to go by, even if it isn't to solve this problem, to act as proof for my boss.
I've solved the bug! I've not read this answer to this problem before so it is pivotal that the Excel community receives this.
Excel fails to call its sub routines in the Application OnTime queue whenever this happens. You have started to edit a cell, stayed in edit mode, and then switched away from Excel either by minimizing the window or by clicking onto another window.
All the sub routines in the Application OnTime queue will wait until the cell is finished editing. So once you switch back to Excel, the cell switches edit mode off, and then all the sub routines will run.
I'm actually quite impressed I solved this myself.

VB .net Infinite Loop/Interrupt Driven

I'm pretty new to VB .net still and I'm attempting to write a program that constantly watches information pulled from a PLC and updates when it changes.
I've worked some with microcontrollers and I know making an infinite loop works well. What I'm running into with my VB .net code is when it loops, the form basically freezes up and the label never updates. I attempted using the code below.
Do While True
*Code*
Loop
So, I guess my first question is, what is the best way to go about having a continuous program to update data. It seems an infinite loop is not the answer.
My second question is, like microcontrollers, is it possible to have VB .net be an interrupt driven program instead of an event driven. My program is always running, updating the data as it comes in, but if a button is pressed it can essentialy break out of the loop. Or what I have in mind, temporarly leave the loop to perform another function then continue back into the loop.
I've looked at the System.Timers namespace, and am not 100% clear on how they work. It seems like it is potential answer to my problem. If I have a timer that does the code every one second, this one second occures as long as the form is running correct? Assuming the previous statement is correct, if the code is running from the 1 second tick, and a button is pushed while that code is running, will the button be ignored, or will the event happen after the code has finished.
Anything to help me learn will be greatly appeciated!
You can use the
Application.DoEvents()
-method provided by the .NET framework, it essentially updates your form to apply all the changes that happened in the meantime.

Create a thread, keep it running, and reuse it / make calls on it

I'm not sure if this is possible or not, and I'm afraid I have no code at the moment, but I'm working on it. I've been searching for techniques like what I'm trying to do, but I'm not sure if anything is out there... let me explain conceptualy what I'm trying to do...
I have a small multi-threaded VB.net application I'm writing. In this application I'm using a com object. Initially I was trying to share this object between multiple threads - no dice. It was breaking on a wait for event line, which was when I learned about message pumping... Which I'm not sure I'm up to.
So, what I would like to try to do is set up a thread with that object and keep it running in a loop waiting for input from one of the other threads. When it gets such input, I'd like it to use the com object to perform a few tasks and then update a private class level collection, and then return to looping until I kill it.
I've been looking around to see if any sort of functionality like this is described anywhere, but I'm coming up dry. I'd most likely be creating this thread as a task... here's sort of what I'm thinking the first part would look like...
Dim openCheckTask As Task
openCheckTask = Task.Factory.StartNew(Sub() openOrderCheck())
...
Sub openOrderCheck()
Dim myComObject As ComObject = New ComObject
Do
Wait for input '<- ? This sort of plays into what I'm looking for...
myComObject.doSomeStuff
SomeCollection.Add(someValue)
Loop
End Sub
I really have no idea how to proceed from there. It's also worth noting that this thread would need to have a cue of commands, it could be executing one and have another or more waiting to be executed... not sure if that is possible either.
Any hints or suggestions are appreciated! Thanks again for your time everyone.

VB.NET Automatic Process Kill?

Recently, this small, annoying, and random process from Yahoo, which I don't even use, has been starting randomly, and will begin taking up around 2000k memory. I have coded something I thought will automatically stop the process when it begins running, although it does not preform correct, nor does it do what I want it to do. This is what I have compiled:
Dim arrProcess() As Process = System.Diagnostics.Process.GetProcessesByName("YahooUIService")
Dim constant As Integer
While constant = 5 'This is in the form
For Each p As Process In arrProcess
p.Kill()
Next
End While
Does anyone know what I am doing wrong, and how I can correct myself? I know VB.NET well, although yes, there are many small things like this that throw me off.
Your code would only detect the app if it was open at the time your app started. To continuously detect the app you'd have to call GetProcessesByName() INSIDE your While loop. Definitely put some kind of delay in there, though, as otherwise you'll ramp up CPU usage to 100%. It'd be better to use a Timer() control and check for the process in the Tick() event.

Excel freezes during execution

When I execute my code (which takes 3-4min. to execute) in Excel, and I click on something in excel or in my userform, it freezes, gets white, almost crashes... When the execution is ready everything is fine again.
Of course it is normal that I can't work in excel during execution, but how can I avoid the "crashing" of excel?
Do you have a Loop in the code?
If so, add the following line just after you initialize the loop and it should help with your problem!
DoEvents
The best way to prevent these kinds of issues is to benchmark your code to determine what is taking so long. If your code is taking over 3 minutes to run, you almost certainly have room for optimization.
Here are the steps you need to take:
Set multiple breakpoints within the code that is executing.
Time how long it takes to get from one breakpoint to the next.
Once you've identified the culprit(s), refactor.
If you aren't able to improve performance, post the inefficient code and ask for help.
A notorious slow-performing anti-pattern to look out for is selecting things during a loop.
What you need to do is create a custom UserForm that will overlay the Excel UI, like a Progress Bar. You'll also want to look into ways to force your form to stay ontop, much like a modal form. You can't use modal form's because they cease execution.
After you make sure there are no infinite loops making excel unresponsive try optimizing your code.
I found this a very interesting read on vba-excel code optimization and some of the practices actually had an impact on a big and time consuming module I wrote. Hope it helps.