Jeanette Durham
asked on
Using WINAPI calls to download a very large file. Fatal error occurs only when I try to do a DoEvents. Need to update a progressBar, as the file downloads.. (VarPtr might be issue)
Dear Experts
Ok, I've got this program, and I'm updating it to .net. The portion of the program with the error is supposed to retreive a file over the internet, and save it to disk. This worked in vb6, and I'm not sure why it's not working here. I feel that it is /very/ close to working, but it isn't quite right. I think there is something wrong with the way I'm trying to compensate for the now non-existant VarPtr function. Anyways, two things are happening. The first, the file just starts at 0k and stays there, and the second is this fatal error which occurs whenever I try to put in any line relating to a 'DoEvents' call. I think when my program does the doEvents, it is losing the handles somehow, or windows is moving them or something. Here is the code for this, any help would be greatly appreciated!
~Michael@iondataexpress.co m
'---------------- solution off of experts exchange
'https://www.experts-exchange.com/questions/22084328/Download-Progress.html
Private Declare Function InternetOpenW Lib "wininet.dll" (ByVal lpszCallerName As Integer, ByVal dwAccessType As Integer, ByVal lpszProxyName As Integer, ByVal lpszProxyBypass As Integer, ByVal dwFlags As Integer) As Integer
Private Declare Function InternetOpenUrlW Lib "wininet.dll" (ByVal hInternet As Integer, ByVal lpszUrl As Integer, ByVal lpszHeaders As Integer, ByVal dwHeadersLength As Integer, ByVal dwFlags As Integer, ByVal dwContext As Integer) As Integer
Private Declare Function InternetReadFile Lib "wininet.dll" (ByVal hFile As Integer, ByVal sBuffer As Integer, ByVal lNumBytesToRead As Integer, ByRef lNumberOfBytesRead As Integer) As Integer
Private Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hInternet As Integer) As Integer
Private Declare Function CreateFileW Lib "kernel32" (ByVal lpFileName As Integer, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, ByVal lpSecurityAttributes As Integer, ByVal dwCreationDisposition As Integer, ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As Integer) As Integer
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Integer, ByVal lpBuffer As Integer, ByVal nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer, ByVal lpOverlapped As Integer) As Integer
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Integer) As Integer
Const GWrite As Integer = 1073741824
Const Create As Integer = 2
Const Context As Integer = 2
Const Multiple As Integer = 1024
Const Reload As Integer = &H80000000
'if the other varPtr doesn't work, use this one
Public Function VarPtr(ByVal o As Object) As Integer
Dim GC As System.Runtime.InteropServ ices.GCHan dle = System.Runtime.InteropServ ices.GCHan dle.Alloc( o, System.Runtime.InteropServ ices.GCHan dleType.Pi nned)
Dim ret As Integer = GC.AddrOfPinnedObject.ToIn t32
GC.Free()
Return ret
End Function
Public Sub httpGet(ByVal File As String, ByVal httpUrl As String, Optional ByRef Kilobytes As Integer = 1)
Dim bytes() As Byte
Dim hOpen As Integer
Dim hConn As Integer
Dim cFile As Integer
Dim bSize As Integer
Dim bRead As Integer
Dim myFile As Scripting.File
Dim handle As Runtime.InteropServices.GC Handle
Dim strPtr As IntPtr
'Dim value As String = "Bob"
'handle = Runtime.InteropServices.GC Handle.All oc(value, Runtime.InteropServices.GC HandleType .Pinned)
''2) Get the address of the pinned object
'Dim strPtr As IntPtr = handle.AddrOfPinnedObject( )
''3) Free the handle:
'handle.Free()
hOpen = InternetOpenW(0, 1, 0, 0, 1)
handle = Runtime.InteropServices.GC Handle.All oc(httpUrl , Runtime.InteropServices.GC HandleType .Pinned)
strPtr = handle.AddrOfPinnedObject( )
hConn = InternetOpenUrlW(hOpen, strPtr, 0, 0, Reload, Context)
handle.Free()
handle = Runtime.InteropServices.GC Handle.All oc(File, Runtime.InteropServices.GC HandleType .Pinned)
strPtr = handle.AddrOfPinnedObject
cFile = CreateFileW(strPtr, GWrite, 0, 0, Create, 0, 0)
handle.Free()
myFile = mainForm.myFSO.GetFile(Fil e)
If cFile <> (-1) And hConn <> 0 Then
bSize = (Kilobytes * Multiple)
ReDim bytes(bSize)
Do
'UPGRADE_ISSUE: VarPtr function is not supported. Click for more: 'mshelp://MS.VSCC.v80/dv_c ommoner/lo cal/redire ct.htm?key word="3677 64E5-F3F8- 4E43-AC3E- 7FE0B5E074 E2"'
If InternetReadFile(hConn, VarPtr(bytes(0)), bSize, bRead) Then
'UPGRADE_ISSUE: VarPtr function is not supported. Click for more: 'ms-help://MS.VSCC.v80/dv_ commoner/l ocal/redir ect.htm?ke yword="367 764E5-F3F8 -4E43-AC3E -7FE0B5E07 4E2"'
WriteFile(cFile, VarPtr(bytes(0)), bSize, 0, 0)
Else
Exit Do
End If
'Application.DoEvents() <<<<------This is the line that errs out.
frmProgress.Refresh()
If frmProgress.ProgressBar1.M aximum >= myFile.Size Then frmProgress.ProgressBar1.V alue = myFile.Size
Loop While bRead <> 0
End If
InternetCloseHandle(hConn)
InternetCloseHandle(hOpen)
CloseHandle(cFile)
End Sub
These are the errors and descriptions for this (interestingly enough, each time I run it, it doesn't always produce the same error:
1) AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
2) FatalExecutionEngineError was detected: The runtime has encountered a fatal error. The address of the error was at 0x7a00d227, on thread 0xc94. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
Ok, I've got this program, and I'm updating it to .net. The portion of the program with the error is supposed to retreive a file over the internet, and save it to disk. This worked in vb6, and I'm not sure why it's not working here. I feel that it is /very/ close to working, but it isn't quite right. I think there is something wrong with the way I'm trying to compensate for the now non-existant VarPtr function. Anyways, two things are happening. The first, the file just starts at 0k and stays there, and the second is this fatal error which occurs whenever I try to put in any line relating to a 'DoEvents' call. I think when my program does the doEvents, it is losing the handles somehow, or windows is moving them or something. Here is the code for this, any help would be greatly appreciated!
~Michael@iondataexpress.co
'---------------- solution off of experts exchange
'https://www.experts-exchange.com/questions/22084328/Download-Progress.html
Private Declare Function InternetOpenW Lib "wininet.dll" (ByVal lpszCallerName As Integer, ByVal dwAccessType As Integer, ByVal lpszProxyName As Integer, ByVal lpszProxyBypass As Integer, ByVal dwFlags As Integer) As Integer
Private Declare Function InternetOpenUrlW Lib "wininet.dll" (ByVal hInternet As Integer, ByVal lpszUrl As Integer, ByVal lpszHeaders As Integer, ByVal dwHeadersLength As Integer, ByVal dwFlags As Integer, ByVal dwContext As Integer) As Integer
Private Declare Function InternetReadFile Lib "wininet.dll" (ByVal hFile As Integer, ByVal sBuffer As Integer, ByVal lNumBytesToRead As Integer, ByRef lNumberOfBytesRead As Integer) As Integer
Private Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hInternet As Integer) As Integer
Private Declare Function CreateFileW Lib "kernel32" (ByVal lpFileName As Integer, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, ByVal lpSecurityAttributes As Integer, ByVal dwCreationDisposition As Integer, ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As Integer) As Integer
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Integer, ByVal lpBuffer As Integer, ByVal nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer, ByVal lpOverlapped As Integer) As Integer
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Integer) As Integer
Const GWrite As Integer = 1073741824
Const Create As Integer = 2
Const Context As Integer = 2
Const Multiple As Integer = 1024
Const Reload As Integer = &H80000000
'if the other varPtr doesn't work, use this one
Public Function VarPtr(ByVal o As Object) As Integer
Dim GC As System.Runtime.InteropServ
Dim ret As Integer = GC.AddrOfPinnedObject.ToIn
GC.Free()
Return ret
End Function
Public Sub httpGet(ByVal File As String, ByVal httpUrl As String, Optional ByRef Kilobytes As Integer = 1)
Dim bytes() As Byte
Dim hOpen As Integer
Dim hConn As Integer
Dim cFile As Integer
Dim bSize As Integer
Dim bRead As Integer
Dim myFile As Scripting.File
Dim handle As Runtime.InteropServices.GC
Dim strPtr As IntPtr
'Dim value As String = "Bob"
'handle = Runtime.InteropServices.GC
''2) Get the address of the pinned object
'Dim strPtr As IntPtr = handle.AddrOfPinnedObject(
''3) Free the handle:
'handle.Free()
hOpen = InternetOpenW(0, 1, 0, 0, 1)
handle = Runtime.InteropServices.GC
strPtr = handle.AddrOfPinnedObject(
hConn = InternetOpenUrlW(hOpen, strPtr, 0, 0, Reload, Context)
handle.Free()
handle = Runtime.InteropServices.GC
strPtr = handle.AddrOfPinnedObject
cFile = CreateFileW(strPtr, GWrite, 0, 0, Create, 0, 0)
handle.Free()
myFile = mainForm.myFSO.GetFile(Fil
If cFile <> (-1) And hConn <> 0 Then
bSize = (Kilobytes * Multiple)
ReDim bytes(bSize)
Do
'UPGRADE_ISSUE: VarPtr function is not supported. Click for more: 'mshelp://MS.VSCC.v80/dv_c
If InternetReadFile(hConn, VarPtr(bytes(0)), bSize, bRead) Then
'UPGRADE_ISSUE: VarPtr function is not supported. Click for more: 'ms-help://MS.VSCC.v80/dv_
WriteFile(cFile, VarPtr(bytes(0)), bSize, 0, 0)
Else
Exit Do
End If
'Application.DoEvents() <<<<------This is the line that errs out.
frmProgress.Refresh()
If frmProgress.ProgressBar1.M
Loop While bRead <> 0
End If
InternetCloseHandle(hConn)
InternetCloseHandle(hOpen)
CloseHandle(cFile)
End Sub
These are the errors and descriptions for this (interestingly enough, each time I run it, it doesn't always produce the same error:
1) AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
2) FatalExecutionEngineError was detected: The runtime has encountered a fatal error. The address of the error was at 0x7a00d227, on thread 0xc94. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
I haven't looked closely at your code but seems like declaring a new thread to update the progressbar should be the way to go...
ASKER
How would you declare a new thread?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Alright, thanks shaprabal, I was able to eventually fix it by creating a new thread, as you suggested. This is my working code, for completion. Maybe someday someone else will need a really good download routine..
Private Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA"(ByVal pCaller As Integer, ByVal szURL As String, ByVal szFileName As String, ByVal dwReserved As Integer, ByVal lpfnCB As Integer) As Integer
Dim myThread As System.Threading.Thread 'This object will allow us to download the file in a seperate thread
Public Sub GetFile(ByRef filePath As String, ByRef fileURL As String)
Dim llRetVal As Integer ', filePath$, fileURL$
llRetVal = URLDownloadToFile(0, fileURL, filePath, 0, 0)
End Sub
Public Sub DoMeter()
Dim myFile As Scripting.File
On Error Resume Next
Do
myFile = mainForm.myFSO.GetFile(cDN CFilePathE XE)
Loop Until Not myFile Is Nothing
On Error GoTo 0
'frmProgress.aut()
Do
' frmProgress.Refresh()
frmProgress.ProgressBar1.V alue = myFile.Size
Application.DoEvents()
Loop Until myFile.Size >= gDNCFileSize
frmProgress.lblDescription .Text = "Finished downloading file!"
End Sub
Public Sub GetDNCDBForReal()
GetFile(cDNCFilePathEXE, cFileURL)
'frmProgress.Visible = True
'myThreadMeter.Start()
'Do
'frmProgress.ProgressBar1. Refresh()
'Loop Until frmProgress.ProgressBar1.V alue >= gDNCFileSize
'Stop
BootAppAndWait(cDNCFilePat hEXE)
End Sub
Public Sub downloadDoNotCallDB()
If modFiles.doesNeedUpdate() = True Then
'If gDNCFileSize = 0 Then Exit Sub
'filePath = App.Path & "\DNC.exe"
frmProgress.Show()
myFunctions.centerForm(frm Progress)
frmProgress.ProgressBar1.M aximum = gDNCFileSize
frmProgress.Text = "Downloading 'DNC.exe'.."
frmProgress.lblDescription .Text = "Downloading latest 'Do Not Call' Database." & vbCrLf & "Saving file to " & cDNCFilePath
myThread = New System.Threading.Thread(Ad dressOf GetDNCDBForReal)
'myThreadMeter = New System.Threading.Thread(Ad dressOf DoMeter)
'myThread.IsBackground = True
myThread.Start()
DoMeter()
'modFiles.GetFile cDNCFilePathEXE, cFileURL
'httpGet(cDNCFilePathEXE, cFileURL)
'Dim myFile As Scripting.File
'On Error Resume Next
'Do
' myFile = mainForm.myFSO.GetFile(cDN CFilePathE XE)
'Loop Until Not myFile Is Nothing
'On Error GoTo 0
Else
MsgBox("Your `Do Not Call Database' is already up to date. Try again in a few days.")
End If
frmProgress.btnClose.Enabl ed = True
mainForm.checkForDNCDB()
End Sub
Private Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA"(ByVal
Dim myThread As System.Threading.Thread 'This object will allow us to download the file in a seperate thread
Public Sub GetFile(ByRef filePath As String, ByRef fileURL As String)
Dim llRetVal As Integer ', filePath$, fileURL$
llRetVal = URLDownloadToFile(0, fileURL, filePath, 0, 0)
End Sub
Public Sub DoMeter()
Dim myFile As Scripting.File
On Error Resume Next
Do
myFile = mainForm.myFSO.GetFile(cDN
Loop Until Not myFile Is Nothing
On Error GoTo 0
'frmProgress.aut()
Do
' frmProgress.Refresh()
frmProgress.ProgressBar1.V
Application.DoEvents()
Loop Until myFile.Size >= gDNCFileSize
frmProgress.lblDescription
End Sub
Public Sub GetDNCDBForReal()
GetFile(cDNCFilePathEXE, cFileURL)
'frmProgress.Visible = True
'myThreadMeter.Start()
'Do
'frmProgress.ProgressBar1.
'Loop Until frmProgress.ProgressBar1.V
'Stop
BootAppAndWait(cDNCFilePat
End Sub
Public Sub downloadDoNotCallDB()
If modFiles.doesNeedUpdate() = True Then
'If gDNCFileSize = 0 Then Exit Sub
'filePath = App.Path & "\DNC.exe"
frmProgress.Show()
myFunctions.centerForm(frm
frmProgress.ProgressBar1.M
frmProgress.Text = "Downloading 'DNC.exe'.."
frmProgress.lblDescription
myThread = New System.Threading.Thread(Ad
'myThreadMeter = New System.Threading.Thread(Ad
'myThread.IsBackground = True
myThread.Start()
DoMeter()
'modFiles.GetFile cDNCFilePathEXE, cFileURL
'httpGet(cDNCFilePathEXE, cFileURL)
'Dim myFile As Scripting.File
'On Error Resume Next
'Do
' myFile = mainForm.myFSO.GetFile(cDN
'Loop Until Not myFile Is Nothing
'On Error GoTo 0
Else
MsgBox("Your `Do Not Call Database' is already up to date. Try again in a few days.")
End If
frmProgress.btnClose.Enabl
mainForm.checkForDNCDB()
End Sub