Progress Bar and File Copying Problem? - vb6

Using VB 6
In my Project, when I copy the file from one folder to another folder, at the time I want to show the progress bar like copying…., Once the file was copied the Progress bar show’s 100 % Completed.
Code.
'File Copying
Private Sub Copy_Click()
Timer1.Enabled = True
Dim abc As Integer
Dim line As String
abc = FreeFile
Open App.Path & "\DatabasePath.TXT" For Input As #abc
Input #abc, line
databasetext = line
Dim fs As New FileSystemObject, f As File
Set f = fs.GetFile(databasetext)
f.Copy App.Path & "\"
Set fs = Nothing
Close #abc
End Sub
Private Sub Timer1_Timer()
ProgressBar1.Min = 0
ProgressBar1.Max = 100
ProgressBar1.Value = ProgressBar1.Value + 1
If ProgressBar1.Value = ProgressBar1.Max Then
Timer1.Enabled = False
End If
End Sub
Above code Is working, But when I click copy button, Progressbar1 is not displaying, once the file was copied to another folder. Then only progressbar1 is stating.
Both will not working simultaneously.
And Also Once the file was copied, then progress bar should display 100 %. Now it is not displaying correctly, Still the file is copying, Progress bar is showing 100 %
Can any one help to solve the problem.
Need VB 6 Code Help.

If the standard copy function is blocking the timer from firing then the best thing you can do is write your own copy which reads the source file a few thousand bytes at a time and writes it to the destination file.
Between each read and write operation you need to update your progress bar and (possibly) call DoEvents to make sure it redraws.
Also your timer code makes no sense. It just arbitrarily increases progress every time if fires without reference to how much progress has actually been made. You would be better off passing the progress bar to your copy function so that it can updated as you go.
Something like this would do it:
Private Sub Copy_Click()
Dim abc As Integer
Dim line As String
abc = FreeFile
Open App.Path & "\DatabasePath.TXT" For Input As #abc
Input #abc, line
copyFile line, App.Path & "\" & line, ProgressBar1
Close #abc
End Sub
Sub copyFile(inFile As String, outFile As String, ByRef pg As ProgressBar)
Close
Const chunkSize = 1024
Dim b() As Byte
fhIn = FreeFile
Open inFile For Binary Access Read As #fhIn
fhOut = FreeFile
Open outFile For Binary Access Write As #fhOut
toCopy = LOF(fhIn) 'gets the size of the file
fileSize = toCopy
pb.Min = 0
pb.Max = toCopy
While toCopy > 0
If toCopy > chunkSize Then
ReDim b(1 To chunkSize)
toCopy = toCopy - chunkSize
Else
ReDim b(1 To toCopy)
toCopy = 0
End If
Get #fhIn, , b
Put #fhOut, , b
pg.Value = fileSize - toCopy
DoEvents
Wend
Close #fhIn
Close #fhOut
End Sub

For a progress bar to function, it either has to be updated inline with a periodic loop, or run in a separate thread.

The copy in old school VB6 is a blocking command. So even DoEvents will give the same result (the file will copy, then the progress bar will show up). If you are copying large files over a slow medium and you need to be able to show progress, then you should create the target file and move over bytes in chunks in a loop, in that loop you could update your progress bar. Sadly for the example given in the OP you won't get what you are looking for since every operation is synchronous.
EDIT: Beaten by the guy above me :)

Related

Need to save pen tool drawigns in powerpoint before exiting

I have working macro for switching mouse cursor to pen tool and back to cursor.
Now the problem is, that after I have done my drawing with pen tool, I use macro to export that page to .pdf and after that quit slideshow with
ActivePresentation.SlideShowWindow.View.Exit
and after pressing macro button PP ask if I want to Keep or Discard my drawing. After pressing Keep, slideshow shut down, new pdf file is created, but drawing doesn't get to in. If I export that page again, then it shows up. So it seems it creates .pdf first and after that saves the drwaing.
So is there a way to save drawing before exiting slideshow? It is fine if drawing is not there after leaving slideshow, but I want it to be in .pdf after first click.
Current macro for pdf export is
Sub convert_to_PDF()
Dim timestamp As Date
Dim PR As PrintRanges
Dim lngLast As Long
Dim lngFirst As Long
Dim savePath As String
Dim PrintPDF As Integer
Dim name As String
Dim originalHides() As Long
Dim slidesToPrint() As Variant
Dim i As Variant
timestamp = Now()
With ActivePresentation
name = .Slides(2).Shapes("TextBox1").OLEFormat.object.Text
savePath = "C:\Powerpoint\" & Format(timestamp, "yyyymmdd-hhnn") & " - " & name & ".pdf"
lngLast = .Slides.Count
.PrintOptions.Ranges.ClearAll
slidesToPrint = Array(2, lngLast)
ReDim originalHides(1 To lngLast)
For i = 1 To lngLast
originalHides(i) = .Slides(i).SlideShowTransition.Hidden
.Slides(i).SlideShowTransition.Hidden = -1
Next
For Each i In slidesToPrint()
.Slides(i).SlideShowTransition.Hidden = 0
Next
.ExportAsFixedFormat _
Path:=savePath, _
FixedFormatType:=ppFixedFormatTypePDF, _
Intent:=ppFixedFormatIntentScreen, _
FrameSlides:=msoTrue
For i = 1 To lngLast
.Slides(i).SlideShowTransition.Hidden = originalHides(i)
Next
EraseInkOnSlide ActivePresentation.Slides(lngLast)
End With
End Sub

How to get saved texts from saved file

I am making a number generator for my own purpose.
I already made it to work with these features:
- Saves the generated number
What features I want :
- When I close it loads the last generated number from the notepad.
here is the code:
Private Const FilePath As String = "C:\Users\sto0007404\Documents\Numbers.txt"
Private CurrentNumber As Long
Private Sub Command1_Click()
CurrentNumber = CurrentNumber + 1
txtRefNo.Text = "EM" & Format(CurrentNumber, String(4, "0"))
End Sub
Private Sub Form_Load()
Dim TextFileData As String, MyArray() As String, i As Long
' Open file as binary
Open "FilePath" For Binary As #1
' Read entire file's data in one go
TextFileData = Space$(LOF(1))
Get #1, , TextFileData
' Close File
Close #1
' Split the data in separate lines
MyArray() = Split(TextFileData, vbCrLf)
For i = 0 To UBound(MyArray())
' Set CurrentNumber equal to the current max
CurrentNumber = Val(Mid$(MyArray(i), 2))
Next
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Dim i As Long
' delete the old file
If Not LenB(Dir(FilePath)) = 0 Then Kill FilePath
'open the file for writing
Open FilePath For Output As #1
For i = 1 To CurrentNumber
Write #1, "EM" & Format(i, String(4, "0"))
Next
'close the file (if you dont do this, you wont be able to open it again!)
Close #1
End Sub
Independent of use binary mode...
open file for output as #1
print #1, "Some text"
close #1
open file for input as #1
line input #1, myvariable
close #1
msgbox myvariable
The flow is the same that you show, so what is the problem ?

Creating a userform that prints textbox input to specific spots in the document

I was working on a system in VBA word. The goal of the system is to replace several different words in a document with input from a text box. So far I have a userform with 12 different text boxes each containing input from a user to replace words in the document. I made a button in the userform to print all the input from the textboxes to the document.
For each textbox I made the following code:
Sub FindAndReplaceAllStoriesHopefully()
Dim myStoryRange As Range
'
'
'Loop replaces everything with <KLANTNAAM> in the document
For Each myStoryRange In ActiveDocument.StoryRanges
With myStoryRange.Find
.Text = "<KLANTNAAM>"
.Replacement.Text = TextBox1.Value
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
Do While Not (myStoryRange.NextStoryRange Is Nothing)
Set myStoryRange = myStoryRange.NextStoryRange
With myStoryRange.Find
.Text = "<KLANTNAAM>"
.Replacement.Text = TextBox1.Value
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
Loop
Next myStoryRange
So far I did this for all 12 textboxes and it works but it isn't smooth. The
button upon getting clicked is calling the function with
Call FindAndReplaceAllStoriesHopefully
I have a few problems which I just cannot fix:
Once the button is clicked and some textboxes are not filled by the user, the marked words like <KLANTNAAM> are still replaced and removed from the document.
The performance of the macro is not great since the same code is copied 12 times.
Once the button is clicked, there is no easy way for the user to undo mistakes typed in the userform since the results are already printed.
I was hoping to get some tips so I can finalize this application.
Something like this:
Private Sub CommandButton1_Click()
Dim numBlank As Long, n As Long, txt As String
Dim bookMarkName As String
numBlank = Me.CountBlanks
If numBlank > 0 Then
If MsgBox(numBlank & " entries are blank!. Continue?", _
vbExclamation + vbOKCancel) <> vbOK Then
Exit Sub
End If
End If
For n = 1 To 4
txt = Me.Controls("Textbox" & n).Text
bookMarkName = "BOOKMARK" & n
FindAndReplaceAllStoriesHopefully bookMarkName, txt
Next n
End Sub
Function CountBlanks() As Long
Dim n As Long, b As Long
b = 0
For n = 1 To 4
If Len(Me.Controls("Textbox" & n).Text) = 0 Then
b = b + 1
End If
Next n
CountBlanks = n
End Function

VBA Clipboard fast read

I have problem with reading data from clipboard at fast rate. I have one program that sends data 10 times per sec to clipboard, and now in VBA I want to recive that data.
Code im using:
Dim clipboard As MSForms.DataObject
Dim strContents As String
Set clipboard = New MSForms.DataObject
Do While True
clipboard.GetFromClipboard
strContents = clipboard.GetText
clipboard.Clear
Myfunc(strContents)
ThisApplication.StatusBarText = strContents 'debug
If Left(strContents, 1) = 9 Then Exit Sub 'end condition
Set clipboad = Nothing
sleep 100
Loop
End Sub
There is problem: this code is working for about 2 sec and then I get error:
DataObject:GetText 8007000E - Ran out of memory.
The clipboard data is non stop this same. Im clearing objects every loop. Where is problem? Im on 64 bit windows 7, 64 bit VBA application, 8 gigs of ram.
As Patrick pointed out, you should move the Set clipboard = Nothing below the loop. Also, there is a typo, as it is currently written as set clipboad = Nothing.
In addition, insert DoEvents after you clear your clipboard. This will make sure that the operating system has time to clear it before you read it again.
Your final code should look somehow like this:
Dim clipboard As MSForms.DataObject
Dim strContents As String
Set clipboard = New MSForms.DataObject
Do While True
clipboard.GetFromClipboard
strContents = clipboard.GetText
clipboard.Clear
DoEvents
Myfunc(strContents)
ThisApplication.StatusBarText = strContents 'debug
If Left(strContents, 1) = 9 Then Exit Sub 'end condition
sleep 100
Loop
Set clipboard = Nothing

Write to file using CopyHere without using WScript.Sleep

I've written a small VBScript to creates a .zip file and then copies the contents of a specified folder into that .zip file.
I copy the files over one by one for a reason (I know I can do the whole lot at once). However my problem is when I try to copy them one by one without a WScript.Sleep between each loop iteration I get a "File not found or no read permission." error; if I place a WScript.Sleep 200 after each write it works but not 100% of the time.
Pretty much I'd like to get rid of the Sleep function and not rely on that because depending on the file size it may take longer to write therefore 200 milliseconds may not be enough etc.
As you can see with the small piece of code below, I loop through the files, then if they match the extension I place them into the .zip (zipFile)
For Each file In folderToZip.Items
For Each extension In fileExtensions
if (InStr(file, extension)) Then
zipFile.CopyHere(file)
WScript.Sleep 200
Exit For
End If
Next
Next
Any suggestions on how I can stop relying on the Sleep function?
Thanks
This is how we do it in VB6. After calling CopyHere on the zip we wait for async compression to complete like this
Call Sleep(100)
Do
Do While Not pvCanOpenExclusive(sZipFile)
Call Sleep(100)
Loop
Call Sleep(100)
Loop While Not pvCanOpenExclusive(sZipFile)
where the helper function looks like this
Private Function pvCanOpenExclusive(sFile As String) As Boolean
Dim nFile As Integer
nFile = FreeFile
On Error GoTo QH
Open sFile For Binary Access Read Lock Write As nFile
Close nFile
pvCanOpenExclusive = True
QH:
End Function
Nice side-effect is that even if zipping fails this will not end up in infinite loop.
The trouble comes when accessing the zip-file when it's closed by zipfldr.dll, that is when pvCanOpenExclusive returns true.
You are correct, CopyHere is asynchronous.
When I do this in a vbscript, I sleep until the count of files in the zip, is greater than or equal to the count of files copied in.
Sub NewZip(pathToZipFile)
WScript.Echo "Newing up a zip file (" & pathToZipFile & ") "
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim file
Set file = fso.CreateTextFile(pathToZipFile)
file.Write Chr(80) & Chr(75) & Chr(5) & Chr(6) & String(18, 0)
file.Close
Set fso = Nothing
Set file = Nothing
WScript.Sleep 500
End Sub
Sub CreateZip(pathToZipFile, dirToZip)
WScript.Echo "Creating zip (" & pathToZipFile & ") from (" & dirToZip & ")"
Dim fso
Set fso= Wscript.CreateObject("Scripting.FileSystemObject")
If fso.FileExists(pathToZipFile) Then
WScript.Echo "That zip file already exists - deleting it."
fso.DeleteFile pathToZipFile
End If
If Not fso.FolderExists(dirToZip) Then
WScript.Echo "The directory to zip does not exist."
Exit Sub
End If
NewZip pathToZipFile
dim sa
set sa = CreateObject("Shell.Application")
Dim zip
Set zip = sa.NameSpace(pathToZipFile)
WScript.Echo "opening dir (" & dirToZip & ")"
Dim d
Set d = sa.NameSpace(dirToZip)
' for diagnostic purposes only
For Each s In d.items
WScript.Echo s
Next
' http://msdn.microsoft.com/en-us/library/bb787866(VS.85).aspx
' ===============================================================
' 4 = do not display a progress box
' 16 = Respond with "Yes to All" for any dialog box that is displayed.
' 128 = Perform the operation on files only if a wildcard file name (*.*) is specified.
' 256 = Display a progress dialog box but do not show the file names.
' 2048 = Version 4.71. Do not copy the security attributes of the file.
' 4096 = Only operate in the local directory. Don't operate recursively into subdirectories.
WScript.Echo "copying files..."
zip.CopyHere d.items, 4
Do Until d.Items.Count <= zip.Items.Count
Wscript.Sleep(200)
Loop
End Sub
You can try accessing the file you've just copied, for example with an "exists" check:
For Each file In folderToZip.Items
For Each extension In fileExtensions
If LCase(oFSo.GetExtensionName(file)) = LCase(extension) Then
zipFile.CopyHere(file)
Dim i: i = 0
Dim target: target = oFSO.BuildPath(zipFile, oFSO.GetFileName(file))
While i < 100 And Not oFSO.FileExists(target)
i = i + 1
WScript.Sleep 10
Wend
Exit For
End If
Next
Next
I'm not sure if target is calculated correctly for this use context, but you get the idea. I'm a bit surprised that this error occurs in the first place... FileSystemObject should be strictly synchronous.
If all else fails, do this:
For Each file In folderToZip.Items
For Each extension In fileExtensions
If LCase(oFSo.GetExtensionName(file)) = LCase(extension) Then
CompressFailsafe zipFile, file
Exit For
End If
Next
Next
Sub CompressFailsafe(zipFile, file)
Dim i: i = 0
Const MAX = 100
On Error Resume Next
While i < MAX
zipFile.CopyHere(file)
If Err.Number = 0 Then
i = MAX
ElseIf Err.Number = xxx ''# use the actual error number!
Err.Clear
WScript.Sleep 100
i = i + 1
Else
''# react to unexpected error
End Of
Wend
On Error GoTo 0
End Sub
The solution we used after much debugging and QA on various windows flavours, including fast and slow machines and machines under heavy CPU load was the following snippet.
Critique and improvements welcome.
We were not able to find a way of doing this without a loop, that is, if you wanted to do some validation or post zipping work.
The goal was to build something that ran reliably on as many windows flavours as possible. Ideally as natively as possible too.
Be advised that this code is still is NOT 100% reliable but its seems to be ~99%. As stable as we could get it with the dev and QA time available.
Its possible that increasing iSleepTime could make it 100%
Points of note:
The unconditional sleep seems to be the most reliable and compatible approach we found
The iSleepTime should not be reduced, it seems the more frequently the loop runs, the higher the probability of an error, seemingly related to the internal operations of the zip/copy process
iFiles is the source file count
The more simplistic the loop was, the better, for example outputting oZippp.Items().Count in the loop caused inexplicable errors that looked like they could be related to file access/sharing/locking violations. We didn't spend time tracing to find out.
It seems on Windows 7 anyway, that the internals of the zipping process use a temp file located in the cwd of the compressed zip folder, you can see this during long running zips by refreshing your explorer window or listing dir with cmd
We had success with this code on Windows 2000, XP, 2003, Vista, 7
You'd probably want to add a timeout in the loop, to avoid infinite loops
'Copy the files to the compressed folder
oZippp.CopyHere oFolder.Items()
iSleeps = 0
iSleepTime = 5
On Error Resume Next
Do
iSleeps = iSleeps + 1
wScript.Sleep (iSleepTime * 1000)
Loop Until oZippp.Items().Count = iFiles
On Error GoTo 0
If iFiles <> oZippp.Items().Count Then
' some action to handle this error case
Else
' some action to handle success
End If
Here is a trick I used in VB; get the length of the zip file before the change and wait for it to change - then wait another second or two. I only needed two specific files but you could make a loop out of this.
Dim s As String
Dim F As Object 'Shell32.Folder
Dim h As Object 'Shell32.Folder
Dim g As Object 'Shell32.Folder
Dim Flen As Long, cntr As Long, TimerInt As Long
Err.Clear
s = "F:\.zip"
NewZipFolder s
Flen = FileLen(s)
Set F = CreateObject("Shell.Application").namespace(CVar(s))
TimerInt = FileLen("F:\MyBigFile.txt") / 100000000 'set the loop longer for bigger files
F.CopyHere "F:\DataSk\DemoData2010\Test.mdf"
Do
cntr = Timer + TimerInt
Do
DoEvents: DoEvents
Loop While cntr > Timer
Debug.Print Flen
Loop While Flen = FileLen(s)
cntr = Timer + (TimerInt / 2)
Do
DoEvents: DoEvents
Loop While cntr > Timer
Set F = Nothing
Set F = CreateObject("Shell.Application").namespace(CVar(s))
F.CopyHere "F:\MynextFile.txt"
MsgBox "Done!"

Resources