Excel Not Responding During Macro - performance

I have a macro that runs on a continous loop 24/7. The computer will occasionally freeze but there is no error in the excel code. The code uses the following in order to run efficiently:
DoEvents
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
startagain:
'code
'calculations, alerts, and screen are updated
GoTo startagain
I also am using what I believe is an efficient method of copying and pasting (a bulk of the code is pasting values and formulas):
Length = Range("C1").Value
Set rng = Sheets("Linked Data").Range("A2:AA" & Length)
Range("A2").Value = rng.Value
I have chagned the processor priority on the computer to "high" for EXCEL.exe, I have the computer performance set to maximum performance, I have disable all un-necessary add-ins, and I have turned off autorecover saving.
Despite all of the above, the computer will sometimes freeze and become unresponsive. Does anyone know anything that can be done to improve the reliability?

Avoid Goto and use Application.OnTime Method to run a procedure repeatively.
You can also think of creating a Automation-addin which may lead to performance imporvement as its compiled code.
Kindly refer this link
You may also refer this link for performance imporvement link

Related

Setting Size of String Buffer When Accessing Windows Registry

I'm working on a VBScript web application that has a newly-introduced requirement to talk to the registry to pull connection string information instead of using hard-coded strings. I'm doing performance profiling because of the overhead this will introduce, and noticed in Process Monitor that reading the value returns two BUFFER OVERFLOW results before finally returning a success.
Looking online, Mark Russinovich posted about this topic a few years back, indicating that since the size of the registry entry isn't known, a default buffer of 144 bytes is used. Since there are two buffer overflow responses, the amount of time taken by the entire call is approximately doubled (and yes, I realize the difference is 40 microseconds, but with 1,000 or more page hits per second, I'm willing to invest some time in optimization).
My question is this: is there a way to tell WMI what the size of the registry value is before it tries to get it? Here's a sample of the code I'm using to access the registry:
svComputer = "." ' Local machine is simply "."
ivHKey = &H80000002 ' HKEY_LOCAL_MACHINE = &H80000002 (from WinReg.h)
svRegPath = "SOFTWARE\Path\To\My\Values"
Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & svComputer & "\root\default:StdRegProv")
oRegistry.GetStringValue ivHKey, svRegPath, "Value", svValue
In VBScript strings are strings. They are however long they need to be. You don't pre-define their length. Also, if performance is that much of an issue for you, you should consider using a compiled instead of an interpreted language (or a cache for values you read before).

Excel vba Application.screenupdating vs Application.visible

I have a vba code that copies certain cells from another excel sheet to active excel sheet. I want the work to complete faster.
So does setting Application.ScreenUpdating to false speed up the task better than changing Application.screenupdating/Application.visible to hidden or do both?
I have no direct answer to your question; but I think that setting Application.visible to false will not enhance the performance; I prefer to use the following code:
Public Sub YK_Start()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
End Sub
Public Sub YK_End()
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
usage example:
Sub LoopExample()
Dim Cell As Range
Call YK_Start
Columns("B:F").ClearContents
For Each Cell In Range("A1:A100000")
Cell.Offset(, 1) = Cell.Value + 1
Cell.Offset(, 2) = Cell.Value + 2
Cell.Offset(, 3) = Cell.Value + 3
Cell.Offset(, 4) = Cell.Value + 4
Cell.Offset(, 5) = Cell.Value + 5
Next Cell
Call YK_End
End Sub
This source code will be executed within18 seconds without using Call YK_Start and Call YK_End; and It will be executed within 10 seconds using these procedures.
Reference: www.officena.net : An Arabic Office Forum.
EDIT #1
There are many ways to measure the execution time of your code; I don't know the most accurate one; I only need approximate value; See:
How do you test running time of VBA code?
I am using the simplest one:
Sub my_test()
Dim t As Single
t = Timer
'code
Call LoopExample
MsgBox Timer - t
End Sub
Which one(/both) of Application.screenupdating/Application.visible should I set as false?
Please provide reasons for your answers.
It really depends on what you want to do. Let me explain.
Purpose
Application.visible is used when you do not want the user to see the application. Whether the application is updating or not is immaterial. For example you may have a Login Userform displayed and you do not want to show the application till the user enters correct information..
Application.screenupdating on the other hand has nothing to do with the visibility of the Application per se. It is there to (in simple words) prevent the flicker that you see when the application is being updated.
Which one should I use?
When the Screenupdating is False, and Application.visible is True then the flicker doesn't happen
When the Screenupdating is True, and Application.visible is False then the flicker happens but you cannot see it as the application is hidden.
When the Screenupdating is True, and Application.visible is True then the flicker happens and you can see it.
When the Screenupdating is False, and Application.visible is False then the flicker doesn't happen.
So if you are worried about performance(speed of execution of code) then it is always advisable to switch Screenupdating to False.
I ran a timer on various combinations of the settings. Here's what I found:
Note: All measurements are in milliseconds, but absolute numbers not important*. It's the relative changes I'd like to point out. Feel free to turn into percentages or ratios. (*I favour GetTickCount/GetTickFrequency Win API functions, but it doesn't matter what you choose to use)
Individual Settings:
Baseline w/ Everything On: 62,182.910
Everything On (but Visible=False): 60,366.117
Everything On (but DisplayAlerts=False): 59,639.975
Everything On (but ScreenUpdating=False): 58,257.545
Everything On (but Calculation=Manual): 42,605.444
Everything On (but EnableEvents=False): 17,127.370
Combination of Settings:
Baseline w/ Everything Off: 8,118.695
Everything Off (but DisplayAlerts=True): 8,802.461
Everything Off (but Visible=True): 8,965.122
Everything Off (but ScreenUpdating=True): 9,912.257
Everything Off (but Calculation=Automatic): 15,931.297
Everything Off (but EnableEvents=True): 41,896.063
In short:
Biggest individual speed-boost comes from EnableEvents=False
Second biggest individual speed-boost comes from Calculation=Manual
Third biggest individual speed-boost comes from ScreenUpdating=False
Even bigger boost comes from combining these three settings
Notable Mentions:
Application.Visible=False alone has no significant effect (assuming, you're not chasing sub-1 second optimisations... If you are, are you sure VBA is the best tool for the job?). (Personally, I use Application.Visible where Excel is executing a long-running/fragile process and I don't want the user to interfere by touching the sheet)
DisplayAlerts=False alone has no significant effect, but it's job is to reduce the chance of your process being interrupted. (I use this for the same reason as hiding the sheet - it helps avoid interference. If nothing happens that's likely to trigger an alert, this setting will not contribute anything significant. That's unless you're chasing millisecond optimisations.)

Determine if a program started through my script is still running without using minmgmts://

Short introduction
I have an SSD and I frequently record movies with Fraps. Unfortunately, my SSD is not that big and my secondary harddrive is not fast enough for Fraps to record movies to. I decided to write a script that will start Fraps for me, and then checks if Fraps is done recording, moves the file to my second harddisk and notifies me of this event.
This works great. The problem is that I want to exit the script when I close Fraps. The only way VBScript seems to be able to do it (at least, thats what everyone recommends doing) is by querying the processlist to see if the program is active. Because Fraps is a heavy program, and I run many programs at the same time, just querying this list creates a lag spike in my recording. It only happens if I have too many programs open, but the programs themselves are not the problem, just the amount.
Given that it creates just one lag spike with one check is already too much so I'm really looking for a different solution if there is any.
Visual Basic
I've programmed a lot in Visual Basic, even though it was a many many years ago, and later using VBA, and in there you can basically start a program, retreive the handle of it, and by that handle alone, check if the application still runs. Does something like that exist in VBScript?
If VBScript won't do it, is there another simple macro scripting program that can check for filesize, execute a program and determine if that program is still running or not at a later point in the script?
The code I currently use for determining if a program runs is this:
Function IsProcessRunning( strComputer, strProcess )
Dim Process, strObject
IsProcessRunning = False
strObject = "winmgmts://" & strComputer
For Each Process in GetObject( strObject ).InstancesOf( "win32_process" )
If UCase( Process.name ) = UCase( strProcess ) Then
IsProcessRunning = True
Exit Function
End If
Next
End Function
Assuming this is on your local computer, have you tried collecting the results off of the command line rather than querying the winmgmts object?
E.g.:
Function IsProcessRunning(strProcess)
Dim objShell, strCommand, objExecObject, strText
Set objShell = CreateObject("Wscript.Shell")
strCommand = "%comspec% /c tasklist"
Set objExecObject = objShell.Exec(strCommand)
Do While Not objExecObject.StdOut.AtEndOfStream
strText = objExecObject.StdOut.ReadAll()
Loop
If instr(UCase(strText), UCase(strProcess)) Then
IsProcessRunning = True
Exit Function
Else
IsProcessRunning = False
Exit Function
End If
End Function
I found out how to do it using the method I used to do in Visual Basic, but using the new things I learned during researching Rich's method.
Even though Rich's method doesn't work, it pushed me into the right direction, so I'm giving him a vote up.
Here's the script for starting Fraps:
Dim oShell, oFraps
Set oShell = WScript.CreateObject( "WScript.Shell" )
set oFraps = oShell.exec("""c:\path\to\fraps\fraps.exe""")
Here's the main script that keeps looping until Fraps has exited.
do while IsProcessRunning()
'your code here
loop
wscript.quit
And here's the function to see if Fraps still runs.
Function IsProcessRunning()
dim result
result = oFraps.StdOut.readall
end function
I just wanted to see if the above thrown in an error and work with an error catcher to do it, but I was very surprised when just this code made my script work! It starts fraps for me, and the script continues to run until I close fraps. I use sapi.speak to notify me when the script starts and ends, and I can keep fraps open for a long time and I don't hear that the script ends. I then close fraps, and moments later the script tells me that it ends.

Return to an already open application when a user tries to open a new instance in vb6

Suppose a user minimize my visual basic application to the taskbar notification icon. Now I want when user open a new instance, the old one should restore.
Generally, the strategy used to create a single-instance application is to add some code to the application initialization that determines whether an instance is already running. If one is, it gets a handle to its main window, passes the focus to it, and silently dies. If one is not, it continues to run and completes the rest of the initialization sequence as usual.
You'll find lots of old VB 6 articles that accomplished this by iterating through all of the top-level windows, looking for one whose caption matches the one you expect. But this is a pretty fragile solution, it doesn't take very much to throw it off.
Same deal with the App.PrevInstance property. This is very simple to use, but also very simple in its implementation. It works based on the name of the executable and looks for a running process whose name is a match. However, this is easily defeated if a user creates and renames a copy of the executable. If this is acceptable for you, you could implement this very easily by querying the App.PrevInstance property. Otherwise, you'll need to use a more robust solution.
One such possibility is to create and register a named mutex when the first instance of your application is starting up. Then, when subsequent instances try to register that same mutex, they will fail, indicating that an instance is already running. You can find instructions on using mutexes in VB 6 in the answers to this question.
A couple of important caveats to using mutexes:
You need to make sure that you call the ReleaseMutex and CloseHandle functions when your application is closed in order to release ownership of and destroy the mutex that you created.
When you are running your program in the VB 6 IDE (e.g., to debug it) and it registers a mutex, the mutex belongs to the IDE and won't be released until you close the IDE and restart it. To prevent this, you can suppress the creation of the mutex when running inside of the IDE/debugger using conditional compilation. If you take this approach, make sure to test your program outside of the debugger to be sure that the mutex-related functionality is working as expected! You should never ship something to customers that you haven't thoroughly tested.
You can find all of the VB 6 declarations for these Windows API functions by using the API Viewer program that comes bundled with your VB 6 installation.
More information about handling multiple instances of a VB 6 application is available here on Karl Peterson's site. There's also a complete example implementation in this article on VB Accelerator—focus specifically at step 2, you don't need the rest of the code.
You can often do this fairly simply using DDE in a degenerate way:
Form1.frm
Option Explicit
'This is Form1. To use as DDE source at design time we set:
' Form1.LinkMode = 1 (Source, i.e. vbLinkSource).
' Form1.LinkTopic = "Form1" (default).
'
'Note we use (hidden) Label1 on this Form as a DDE destination.
Private PrevState As Integer
Private Sub Form_LinkExecute(CmdStr As String, Cancel As Integer)
'Got a "command" so restore Form1 and accept the command.
WindowState = PrevState
Caption = "I am awake!"
Cancel = False
End Sub
Private Sub Form_Load()
PrevState = WindowState
End Sub
Private Sub Form_Resize()
If WindowState <> vbMinimized Then PrevState = WindowState
End Sub
Module1.bas
Option Explicit
Private Sub Main()
Load Form1
'After Form1 is loaded (hidden), try DDE link to possible prior copy.
With Form1.Label1
.LinkTopic = App.EXEName & "|Form1"
On Error Resume Next
.LinkMode = vbLinkManual
If Err.Number = 0 Then
On Error GoTo 0
'Link succeeded. Wake up prior copy via pushback to
'the DDE source, then unload Form1 and terminate.
.LinkExecute "Wake up!"
Unload Form1
Else
On Error GoTo 0
'Link failed, so we're 1st. Show Form1.
Form1.Show vbModal
End If
End With
End Sub

Is there any way to tell if the Windows Directory is writeable without actually writing to it to test?

I have some old vb6 code that checks to see if the Windows directory is writeable by WRITING to it then reading a value back.
But... we have a virus scanner that's viewing that as suspicious behavior so I want to check it without touching it.
Any Windows API calls for that? (Ideally for Win 98 and above)
Something to remember here is that the file system is volatile. About the only way I can see this code being used is to first do a check if a folder is writable, and then try to write something you wanted to write. The problem here is that with a volatile file system things might change in between when you make your check and when you try to write. As a consequence, you still have to be able to handle an exception if your write fails. That means the initial check is pretty much wasted. Better to put your effort into writing a better exception handler.
Additionally, for windows 2000 and later the Windows directly should only ever be writable if the user is running as an administrator. For a very long time running as an administrator was common practice, but people are starting to get the hint that this isn't a good idea. Long term, it's not a good idea for your program to do anything that requires running that way.
In fact, starting with Windows Vista, the user doesn't run anything as administrator by default, even when logged in to the administrator account. Instead, they have to manually choose to run the program as administrator or wait a security check to fail the system can prompt them to elevate.
If you have the VB6 code, you should take the time to fix it so that it does NOT need to write to the Windows directory at all because regardless of whether or not you are an administrator - unless you work at Microsoft you should consider that directory off limits.
However, you should consider that on Windows 98, the user will always have the ability to write to the Windows directory. On Windows XP, local administrators will. On Windows Vista and Seven, even administrators will not unless your application has been elevated.
So you can check for whether or not the user is in the built-in role BUILTIN\Administrators using CheckTokenMembership. This will be false for non-admins or non-elevated processes. It does not guarantee you can write to the Windows directory but it will be right most of the time. You can then add error handling logic for when the call actually fails.
But again, you should take the opportunity to fix the code and not use the Windows directory.
For Windows 2000 and above you could use GetNamedSecurityInfo() and AccessCheck(), but I would imagine those are a pain to call from VB.
Here is a function that will do it. I adapted this from some other code kind of quickly so if you use it you need to add error handling, (for instance a directory that doesn't exist just returns False. I have no idea if your anti-virus software is going to like this or not.
Function FolderIsReadOnly(ByVal FolderSpec As String) As Boolean
Dim rst As Long
Dim udtW32FindD As WIN32_FIND_DATA
Dim lngFHandle As Long
Dim strFolder As String 'set to FolderSpec parameter so I can change it
If Len(FolderSpec) = 0 Then
FolderIsReadOnly = False
Exit Function
End If
strFolder = FolderSpec
If Right$(strFolder, 1) <> "\" Then
strFolder = strFolder & "\"
End If
strFolder = strFolder & "*" 'add the wildcard allows finding share roots
lngFHandle = FindFirstFile(strFolder, udtW32FindD)
If lngFHandle <> INVALID_HANDLE_VALUE Then
Call FindClose(lngFHandle)
FolderIsReadOnly = (udtW32FindD.dwFileAttributes And FILE_ATTRIBUTE_READONLY) = FILE_ATTRIBUTE_READONLY
End If
End Function
Function IsPathAccessible(ByVal sPath As String) As Boolean
On Error GoTo ErrHandler
FileSystem.SetAttr sPath, vbNormal
IsPathAccessible = True
Exit Function
ErrHandler:
IsPathAccessible = False
End Function

Resources