Ok, so the code below is just an example of how I'm progressing my progress bar.
I've been adjusting the following line: System.Threading.Thread.Sleep(100) (fast process) or System.Threading.Thread.Sleep(2000) (slower process).
Is there anyway to adjust this on the fly?
A little background info might help. On a daily basis, I create PDFs ranging from a couple hundred to even a couple thousand. Then, all PDFs get zipped up and transferred via SFTP. The PDF sizes could also vary.
Obviously my System.Threading.Thread.Sleep(100) would be on the lower end if I only had a handful of small PDFs to process.
Which leads me back to my original question, can that sleep number be adjusted on the fly based on number of PDFs and the size of all of them?
Or do I need to process a test run and find that magical number to use?
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim ZipPDFs As BackgroundWorker = CType(sender, BackgroundWorker)
Dim i As Integer
For i = 1 To 10
If (ZipPDFs.CancellationPending = True) Then
e.Cancel = True
Exit For
Else
' Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(100)
ZipPDFs.ReportProgress(i * 10)
End If
Next
End Sub
You got the example wrong! The sleep there, is to demostrate a real time consuming operation. You need to take it out, and write the actual operations that you need to perform ;)
Related
I have a VB script that does row processing on a small Excel file (35 columns, 2000 rows, no hidden content). It takes about 3 seconds to process the file when applied to an .xls file. If I save the .xls as a .xlsx file, the same script takes about 30 to 60 seconds, completely freezing Excel several times in the process (although it finishes and the results are correct).
I suspect some loop parts of the code are responsible, but I need to find out which. I have found scripts like https://stackoverflow.com/a/3506143/5064264 which basically amount to timing all parts of the script by hand - similar to printf-debugging, which is not very elegant and also consumes much time for large scripts.
Is there a better alternative to semi-manual profiling, like performance analyzers in most IDEs, or exist any other approaches to this problem? I do not want to post the code as it is rather long and I also want to solve it for other scripts in the future.
yes - kind of. I use this to test how fast things are running and experiment with different approaches to get better timings. I stole this from somewhere here on SO.
sub thetimingstuff()
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
'your code goes here
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
end sub
Another EDIT
you could bury debug.print lines sporadically through your code to see how much each chunk takes to execute.
The error in my case was as Comintern's comment suggested - a method was copying whole columns into a dictionary to reorder them, instead of just the cells with values in them. To preserve this for future readers, I am copying his/her excellent comment here, thanks again!
An xls file has a maximum of 65,536 rows. An xlsx file has a maximum of 1,048,576 rows. I'd start by searching for .Rows. My guess is that you have some code iterates the entire worksheet instead of only rows with data in them.
As for generic profiling or how to get there, I used a large amount of breakpoints in the editor (set/remove with left-click in the line number column of the code editor):
I first set them every 20 lines apart in the main method, ran the program with the large dataset, then after each press of continue (F5) manually measured if it was slow or not.
Then I removed the breakpoints in the fast regions and added additional ones in the slow regions to further narrow it down into methods and then into lines inside the methods.
At the end, I could verify if by commenting out the responsible line of code and fix it.
I have an excel file on a shared drive used by 6/7 people, however only one person can edit at a time. The frustration comes when one person opens the file for editing then disappears for lunch for an hour, leaving the excel file open and un-editable for other users.
Is it possible for VBA to listen for when a station is locked, and activate a macro accordingly?
Sorry I am not posting any of my own attempts as I'm a bit of a fish out of water with this level of VBA.
Any points that may help get me started would be really useful.
You have a few options:
Kill the co-workers who do this
Have other users create a copy and save-as to then merge latter (quite hacky)
Or you try a timeout - so if the user selects nothing i 10 minutes the workbook closes. Selecting lock would be a issue with security I think and windows wouldnt let you have that kind of power.
So to timeout call a function every ten minutes to check if user has selected any other cells in the worbook.
If difference("n", lastaction , Now) > 10 Then
ThisWorkbook.Close SaveChanges:=True
End If
You can use NOW function in vba to find current date and time and the work out difference with when an action was made to find the value of 'lastaction'. To do this use:
Sub AnAction(ByVal Sh As Object, ByVal Target As Range)
lastaction = now
End Sub
Hopefully that answers your question.
I have very limited programming experience (i.e. mainly VB in Microsoft Excel and a bit of Visual Studio) so looking for help on how to create a program which is in effect a timer.
I have a boolean cell in Excel (0 or 1) which changes throughout the day. What I want to do is measure the time in a day for which this is 1. What is the easiest way to do this?
I tried doing it in Excel but came across several issues such as constant refreshing needed as it wouldn't pick up when the cell value changed. From years ago having used a bit of Visual Studio I thought this could be a good solution but struggling to understand how to get the cell value imported.
Any help would be greatly appreciated. Thanks.
Can't you simply handle the Worksheet_Change event. Something along the following lines:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngMyCell As Range
Set rngMyCell = Me.Range("A1") ' or whatever cell you're monitoring
If Not Intersect(Target, rngMyCell) Is Nothing Then
If rngMyCell.Value = 1 Then
' Start Stopwatch if it's not running
Else
' Pause Stopwatch if it's running
End If
End If
End Sub
We have a VB6 program that does some string processing in a loop (approximately 500 times) and appends info to a textbox. Each iteration on the loop includes basic operations like Trim, Len, Left, Mid, etc, and finally appends the string to a textbox (the whole form is still invisible at this point). Finally, after the loop, the code calls Show on the form.
On Windows XP, these 500 loops take about 4 seconds. On Windows 7, the exact same code runs in about 90 seconds.
Any suggestions on how to fix this?
Thanks.
I'm guessing you append the text box on each loop iteration... If you can, store everything in a variable and append it to the TextBox once after the loop is finished. Displaying text in a text box takes a lot of time in VB6.
EDIT:
After further investigation and testing, I came to a conclusion that performance on directly assigning strings to the Text property of a TextBox degrades dramatically when the length of the control reaches maximum. The maximum on my PC is 65535 for some reason, even though according to MSDN its
Windows NT 4.0, Windows 2000, Windows 2000 Professional, Windows 2000 Server, Windows 2000 Advanced Server, Windows XP Home Edition, Windows XP Professional x64 Edition, Windows Server 2003 Platform Note: For single line text box controls, if the MaxLength property is set to 0, the maximum number of characters the user can enter is 2147483646 or an amount based on available memory, whichever is smaller
Basically what seems to be happening, if you keep adding text to the TextBox each iteration, it isn't that slow until you reach maximum. What's even more puzzling, when you try to add text beyond the maximum, there will be no errors, but performance degrades significantly.
In my test loop I go from 0 to 12773 I have this:
Text2.Text = Text2.Text + CStr(a) + " "
So when the loop is completed in 4 seconds, the Text2.Text is 65534 characters long. Now, when I double the loop to go beyond the maximum allowed length of the TextBox, it takes three times as much time to complete it.
12773 - 4 seconds
12773*2 - 16 seconds
After realizing this, my first thought was to replace the TextBox with a RichTextBox. But the performance of the latter is even worse. This is assuming you update it every iteration.
It seems that you are stuck with a dilemma - suffer slow performance or change the code to update text box only once after the loop is completed. Further, due to TextBox's maximum length limit, I recommend switching to a RichTextBox or depending on the purpose of this - some other object.
I hope my findings are helpful - it has certainly been fun finding out all these little programming quirks.
Try LockWindowUpdate to switch off updating for your form.
Declare Function LockWindowUpdate Lib "user32" (ByVal hWnd As Long) As Long
'To turn it on just call it like this, passing it the hWnd of the window to lock.
LockWindowUpdate Form1.hWnd
'intensive updating here
'to turn it off just call it and pass it a zero.
LockWindowUpdate 0
From here
I would recommend that you find out exactly what is slow. Redo your timings before and after the string concatenation, and then before and after the copy of the string into the text box. Have the Ole Automation string operations somehow become slower, or has the copying of text into a VB text box become slower?
Once you know this, we can continue with phase 2 ... :-)
Making a small app, and I want a function to execute 50% of the time. So if I were to dbl click the exe half the time the function would execute, and the other half it wouldn't. I can't seem to find anyway to easily do this, the one solution I tried seemed to determine the chance on compile rather than on run. Thanks in advance!
Generate a random decimal number between 0 and 1. If it is greater than 0.5 run, if it is less than or equal to 0.5 do not run.
Don't forget to seed the randomizer! Otherwise it will always give you the same value every time. You seed it using "Randomize Timer", e.g.:
Private Sub Main()
Randomize Timer
If Rnd > 0.5 Then
ExecuteFunction ()
End If
End Sub
For example:
Private Sub Main()
If Rnd > 0.5 Then
ExecuteFunction ()
End If
End Sub
If you want it to randomly run, others have already provided that solution. If you want a more deterministic behavior (it must run exactly every second time), you will need to store state between executions.
You can save the state in either the registry or on the file system by (for example) attempting to read an integer from a file (set it to zero if the file's not there), add 1 and write it back to the same file.
If the number written back was even, run your function otherwise exit.
That way, you'll alternate between execute and don't-execute.