I am new to vb6 since I always use vb2010. I am debugging an inventory software running in vb6 and couldn't find out how to fill datalist control using recordset.
The software use this code, it uses listbox...
with rs
if .RecordCount then
.MoveLast
.MoveFirst
For Counter=1 to .RecordCount
Me.ListBox1.AddItem rs!ProductName
.MoveNext
Next
End If
.Close
End With
But the software database has now thousand of products and the program loads the data so slow
so I tried this:
Using Datalist
With DataList1
Set .RowSource = rs
.ListField = "ProductName"
End With
my code runs with no errors but no data will appear in the control.
Can anyone solve this problem for me.
thank you in advance
The problem is the MoveLast call which iterates over the entire recordset solely for the purpose of populating RecordCount. Use this instead:
With rs
Do While Not rs.eof
Me.ListBox1.AddItem rs!ProductName
.MoveNext
Loop
.Close
End With
This code will speed up your call by removing the unnecessary traversing of the recordset, but having thousands of items in a listbox is a design/usability issue that speeding the load will not fix.
Related
Below is a piece of code which runs a stored procedure and returns 1 row.
If I run the below piece of code EOF returns true in some cases, even when I know there is data. Sometimes it works correctly sometimes not.
set objConn = server.createobject("ADODB.Connection")
objConn.open funcReadIntranetConnection
set rs = objConn.execute(strSql)
if not rs.eof then
else
end if
I am running those code twice after each other, not sure if that is related, but I recreate all the connections etc each time.
Any suggestions would be much appreciated.
I would suggest:
Do while not rs.eof
'your code here
Loop
I have lots of sub routines and functions like these:
Sub LogInfo(Txt)
'Write an entry to the log file
If LogEnableInfo Then Log "Info: " & Txt
End Sub
Or this:
Function GetSettingValue(Key)
'Get the value of a setting or an empty string if the setting is not set
Dim Res
If Settings.Exists(UCase(Key)) Then Res = Settings(UCase(Key)) Else Res = ""
GetSettingValue = Res
End Function
Or this:
Sub DoExp(Exp, ErrTag)
'Execute an HS.Exp with Error handling and Debugging
Err.Clear
On Error Resume Next
HS.Exp Exp
If Err.Number <> 0 Then
LogError "HS.Exp """ & Exp & """ , Tag: " & ErrTag
Err.Clear
ElseIf LogEnablePut Then
Log "Put: HS.Exp """ & Exp & """ , Tag: " & ErrTag
End If
On Error GoTo 0
End Sub
They're really one liners that don't do much, but I use lots of times, so I don't want to type it out every time. I also use recursive functions a lot.
Since speed and memory use is critical to my application, what I would like to know is if the act of calling a function or sub in vbscript has a considerable performance impact? I know that in PHP, this has been cited as a performance issue. Can I just go ahead and create zillions of little nested functions to do everything or should I use them a little more sparingly?
In my experience using functions repeatedly is a bad thing in VBS, in a small test script I wrote I found that using functions were about 5 times slower.
startTime = Timer
x = 0
For i = 0 To 1000000000
x = x + 1
Next
endTime = Timer
WScript.Echo x
WScript.Echo CStr(endTime - startTime)
startTime = Timer
y = 0
For i = 0 To 1000000000
y = fooBooGoo(y)
Next
endTime = Timer
WScript.Echo y
WScript.Echo CStr(endTime - startTime)
Function fooBooGoo(p)
g = p + 1
fooBooGoo = g
End Function
The output I got was:
1000000001
306.6289
1000000001
1554.875
The rule "violate good practice (don't repeat yourself, clean program structure, ...) for speed gains!" is bad.
I doubt that there is even one real life VBScript program that becomes fast enough as soon as you inline functions/subs/methods - if you show me two, I'm willing to discuss using/writing a preprocessor for VBScript that expands macros and adds line numbering to scripts.
If speed is important, you should benchmark early/continously and optimize for speed by choosing better algorithms or better tools (COM, Net, Shell).
If (3) does not help, switch to a better language.
Samples to illustrate what I mean by "better tools" (#3):
using one ADO connection (created once (with late binding in VBScript)) to execute many SQL statements on a folder of .CSVs instead of using a FSO, .Readline loops, Splits, and complex IFs
using one .Net component (ArrayList, StringBuilder, ...) for many operations instead of using ReDim Preserve on native arrays or string concatenation in a loop
shelling out to dir /s /b c:\where\ever\*.vbs instead of a (faulty/inefficient) homegrown/stolen recursive directory walker that looks at all files to filter the .vbs
Functions are used to carry on a specific task or to perform specific operation. Same as in other languages, but in OOP they are usually called "methods", at least when they are attached to a class. It means to be able to work. Function is the process where a machine can work and make things efficient. here's an ex. that is used in a sentence. the machine won't work right. Or what you could say is the function is the ability to work so the mathematical definition would be the ability to solve an equation/problem. Function means working.
So, with regard to using function re-cursive's would be better but overall in object based languages like vbscript, objects should be created and set=nothing later for actually making system consume memory efficiently.
Thanks
I'm using VBscript to open Microsoft Excel and convert xls documents to csv.
Here is a quick example that takes an argument and converts the first page
Dim oExcel
Dim oBook
Set oExcel = CreateObject("Excel.Application")
Set oBook = oExcel.Workbooks.Open(Wscript.Arguments.Item(0))
oBook.SaveAs "out.csv", 6
oBook.Close False
oExcel.Quit
If everything works, that's great. But when the script crashes before it can close excel, the process continues to stay around and lock the file until I manually kill the process.
How can I make sure that I perform any clean up routines even when the script fails?
As the question is/was? about closing Excel reliably when the VBScript used to automate it crashes:
If you write
Set oExcel = CreateObject("Excel.Application")
you create a 'simple' variable. VBScript may decrement a ref counter when the variable goes out of scope, but it certainly won't .Quit Excel for you.
If you want a feature like atexit calls or exception handling in VBScript, you'll have to write a class that 'does what I mean' in its Class_Terminate Sub. A simple example:
Option Explicit
Class cExcelWrapper
Private m_oExcel
Public Sub Class_Initialize()
Set m_oExcel = CreateObject("Excel.Application")
End Sub
Public Sub Class_Terminate()
m_oExcel.Quit
End Sub
Public Default Property Get Obj()
Set Obj = m_oExcel
End Property
End Class
Dim oExcel : Set oExcel = New cExcelWrapper
Dim oWBook : Set oWBook = oExcel.Obj.WorkBooks.Add()
oExcel.Obj.Visible = True
oExcel.Obj.DisplayAlerts = False
oWBook.Sheets(1).Cells(1,1) = "Div by Zero"
WScript.Echo "Check TaskManager & Enter!"
WScript.StdIn.ReadLine
WScript.Echo 1 / 0
(meant to be started with "cscript 20381749.vbs")
If you run this script with an open Taskmanager, you'll see Excel popup in the processes list (and on the screen, because of .Visible). If you then hit Enter, the script will abort with an "Division by Zero" error and the Excel process will vanish from the Processes list.
If you remove the .DisplayAlerts setting, Excel will ask you whether to save your work or not - proving thereby that the .Quit from the Class_Terminate() Sub really kicks Excel into byebye mode.
The class needs further work (basic settings, common actions (save?) before .Quit, perhaps a guard against misuse (Set oExcel = Nothing or other cargo cult crap), th .Obj addition isn't nice, and it won't help you if you kill your .vbs in a debugger, but for standard scenarios you won't see Excel zombies anymore.
Add "On Error Goto ErrorHandler" at the top of your script, and at the bottom of it, add "ErrorHandler:". Underneath the ErrorHandler label add code to manage the situation depending on the Err.Number
See Err object on MSDN.
You can also use "On Error Resume Next". Here is an example.
EDIT: My bad. "Goto" does not exist in VBS. The answer below is probably a much tidier approach.
In VB6, I'm supporting code that loops through all the views in a Lotus Notes database thusly:
For lngdomViewidx = LBound(domDatabase.Views) To UBound(domDatabase.Views)
Set domView = domDatabase.Views(lngdomViewidx) ' note: this line right here is slow to execute
The amount of time it takes to retrieve a reference to the view by index with this method seems proportional to the number of documents in the view. This bit of code is just looping through the Notes database to build a list of all the view names. On really large databases this can take several minutes. Is there a faster way to get this information?
Thanks!
That is not a very efficient way, no.
Use something like the code below instead. It is Lotusscript, but it should be pretty much the same in VB. I haven't tested it, just copied it from a production database and modified it to look for views instead for forms like in my original...
Dim ncol As NotesNoteCollection
Set ncol = db.CreateNoteCollection(True)
Call ncol.SelectAllNotes(False)
ncol.SelectViews = True
Call ncol.BuildCollection
noteID = ncol.GetFirstNoteId
For i = 1 To ncol.Count
Set doc = targetdb.GetDocumentByID(noteID)
MsgBox "view = " + doc.GetItemValue("$Title")(0)
noteID = ncol.GetNextNoteId(noteID)
Next
Karl-Henry is absolutely right: The NotesNoteCollection is really the fastest way to loop through all views.
But you can speed up your code significantly by just changing the loop.
Instead of opening each view using its index, you coud, do something like:
Forall view in db.Views
'Do whatever you want
End Forall
Accessing elements in a collection in Lotus Notes using the index means, that it always has to count from the beginning, while using a forall- loop directly accesses the next element...
Part of my code copys info from one worksheet in Excell to another. Every time I run this cood it gives me a run time error '1004' for the last line of code (activeSheet.Paste). Does anyone know how to fix it?
Sub CopyData()
ActiveWindow.SmallScroll Down:=-9
Sheets("sponsor & contributions 2012").Select
Range("A1:K93").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("remaining payments").Select
Selection.PasteSpecial Paste:=xlPasteFormats
ActiveWindow.SmallScroll Down:=-12
Range("A1:K93").Select
ActiveSheet.Paste
End Sub
Rather than simulating a user 'copy+paste', it's easier, quicker and more robust to set the value of your target range to that of your source range. e.g.
Public Sub Copy()
Sheet2.Range("A1:K93").Value = Sheet1.Range("A1:K93").Value
End Sub
Determining the cause of a 1004 error can be very difficult without being able to look over the spreadsheet, so I would definitely recommend the above approach