Link to home
Start Free TrialLog in
Avatar of Jeanette Durham
Jeanette DurhamFlag for United States of America

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.com

'---------------- 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.InteropServices.GCHandle = System.Runtime.InteropServices.GCHandle.Alloc(o, System.Runtime.InteropServices.GCHandleType.Pinned)
        Dim ret As Integer = GC.AddrOfPinnedObject.ToInt32
        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.GCHandle
        Dim strPtr As IntPtr

        'Dim value As String = "Bob"
        'handle = Runtime.InteropServices.GCHandle.Alloc(value, Runtime.InteropServices.GCHandleType.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.GCHandle.Alloc(httpUrl, Runtime.InteropServices.GCHandleType.Pinned)
        strPtr = handle.AddrOfPinnedObject()
        hConn = InternetOpenUrlW(hOpen, strPtr, 0, 0, Reload, Context)
        handle.Free()

        handle = Runtime.InteropServices.GCHandle.Alloc(File, Runtime.InteropServices.GCHandleType.Pinned)
        strPtr = handle.AddrOfPinnedObject
        cFile = CreateFileW(strPtr, GWrite, 0, 0, Create, 0, 0)
        handle.Free()

        myFile = mainForm.myFSO.GetFile(File)

        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_commoner/local/redirect.htm?keyword="367764E5-F3F8-4E43-AC3E-7FE0B5E074E2"'
                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/local/redirect.htm?keyword="367764E5-F3F8-4E43-AC3E-7FE0B5E074E2"'
                    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.Maximum >= myFile.Size Then frmProgress.ProgressBar1.Value = 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.
Avatar of shahprabal
shahprabal
Flag of United States of America image

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...
Avatar of Jeanette Durham

ASKER

How would you declare a new thread?
ASKER CERTIFIED SOLUTION
Avatar of shahprabal
shahprabal
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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(cDNCFilePathEXE)
        Loop Until Not myFile Is Nothing
        On Error GoTo 0
        'frmProgress.aut()
        Do
            ' frmProgress.Refresh()
            frmProgress.ProgressBar1.Value = 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.Value >= gDNCFileSize
        'Stop
        BootAppAndWait(cDNCFilePathEXE)
    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(frmProgress)
            frmProgress.ProgressBar1.Maximum = 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(AddressOf GetDNCDBForReal)
            'myThreadMeter = New System.Threading.Thread(AddressOf 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(cDNCFilePathEXE)
            '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.Enabled = True
        mainForm.checkForDNCDB()
    End Sub