Question

Finally FTP Wininet Async Callback, BUT...

Asked by: egl1044

Hello, By Default the WinInet API is Sync which doesn't allow your form to be responsive at all until the file is downloaded, I have managed to finally get WinInet API working in Async operation using the Microsoft article below. I need to know if I set this up correctly, I need expert input on my code. Initially there is always a small delay until it finally gets into the callback proc. But this is much better than it blocking it all together, after about a 3 second delay it will download async and you can perform other operations on the same form, etc.. populate listbox's and such while its downloading..

INFO: Using WinInet APIs Asynchronously Within Visual Basic
http://support.microsoft.com/kb/q189850/

When leaving the callback proc empy it seems the form errors our with (not responding) but its still performs async operations. So I had to figure out someway to make the form stop from saying (Not Responding). By adding Doevents call into the CallBack Proc seems to work without problems.
If anyone has done this before could you shed some light on issue. As for now it works perfect when adding DoEvents into the Callback proc, but there has to be something I am doing wrong. Thanks - egl

* Feel free to try out this code and see for yourself *

'------------ FTPasync.cls ----------------

Option Explicit
'http://support.microsoft.com/kb/q189850/

Private Declare Function InternetCloseHandle Lib "wininet" (ByRef hInet As Long) As Long

Private Declare Function InternetOpen Lib "WININET.dll" Alias "InternetOpenA" _
   (ByVal sAgent As String, _
    ByVal lAccessType As Long, _
    ByVal sProxyName As String, _
    ByVal sProxyBypass As String, _
    ByVal lFlags As Long) As Long

Private Declare Function InternetConnect Lib "WININET.dll" Alias "InternetConnectA" _
   (ByVal hInternetSession As Long, _
    ByVal sServerName As String, _
    ByVal nServerPort As Integer, _
    ByVal sUsername As String, _
    ByVal sPassword As String, _
    ByVal lService As Long, _
    ByVal lFlags As Long, _
    ByVal lcontext As Long) As Long

Private Declare Function InternetSetStatusCallback Lib "WININET.dll" _
   (ByVal hInternetSession As Long, _
    ByVal lpfnInternetCallback As Long) As Long

Private Declare Function FtpGetFile Lib "WININET.dll" Alias "FtpGetFileA" _
   (ByVal hFtpSession As Long, _
    ByVal lpszRemoteFile As String, _
    ByVal lpszNewFile As String, _
    ByVal fFailIfExists As Boolean, _
    ByVal dwFlagsAndAttributes As Long, _
    ByVal dwFlags As Long, _
    ByVal dwContext As Long) As Boolean

Event AsyncDownloadComplete()

Sub DownloadAsync(RemoteFile As String, _
                    LocalFile As String, _
                    HostAddr As String, _
                    UserID As String, _
                    Password As String)
   
   Const nFlag As Long = 0
   Const dwType As Long = 0
   Const lcontext As Long = 2
   Const lAccess As Long = 1
   Const dContext As Long = 2
   Const FTP_DIRECT As Long = 1
   Const FTP_SERVICE As Long = 1
   Const FTP_RELOAD As Long = &H80000000
   Const uAgent As String = "ASYNC"

        hOpen = InternetOpen(uAgent, lAccess, vbNullString, vbNullString, FTP_DIRECT)
        hConnection = InternetConnect(hOpen, HostAddr, 21, UserID, Password, FTP_SERVICE, nFlag, lcontext)

   If hConnection <> 0 Then
        lRet = InternetSetStatusCallback(hConnection, AddressOf MyCallBack)
        lRes = FtpGetFile(hConnection, RemoteFile, LocalFile, False, FTP_RELOAD, dwType, dContext)
   End If
       
        lRes = InternetCloseHandle(hConnection)
        lRes = InternetCloseHandle(hOpen)
       
        RaiseEvent AsyncDownloadComplete
End Sub



'----------- modCallback.bas -----------------

Option Explicit

Public hOpen As Long
Public hConnection As Long
Public lRet As Long
Public lRes As Long

Public Sub MyCallBack(ByVal hOpen As Long, _
            ByVal dwContext As Long, _
            ByVal dwInternetStatus As Long, _
            ByVal lpdwStatusInformation As Long, _
            ByVal dwStatusInformationLength As Long)
           
    Form1.Caption = "Downloading ASYNC NOW!"
           
    DoEvents 'This works to avoid form error NOT RESPONDING!
           
End Sub


'Enter FTP settings below:
'------------- Form1 ----------------

Option Explicit

Private WithEvents FA As FTPasync

Private Sub Command1_Click()

Call FA.DownloadAsync("ProjectBackups.zip", _
                        "D:\ProjectBackups.zip", _
                        "ftp.server.com", _
                        "username", _
                        "password")

End Sub

Private Sub FA_AsyncDownloadComplete()
MsgBox "Download Complete"
End Sub

Private Sub Form_Load()
If FA Is Nothing Then Set FA = New FTPasync
End Sub

Private Sub Form_Unload(Cancel As Integer)
If Not FA Is Nothing Then Set FA = Nothing
End Sub

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2005-10-11 at 14:45:37ID21591651
Tags

wininet

Topic

Visual Basic Programming

Participating Experts
2
Points
500
Comments
8

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. Classes / Withevents
    I am using VB5. I have a class that contains two 'WithEvents' items, declared this way: Private WithEvents mControl As CommandButton Private WithEvents mForm As Form Then I have some properties for the control. Anyway, in the keydown event of 'mForm' ('mForm' is the paren...
  2. Keyboard Callback
    Does anyone have an example for the SetWindowsHookEx for the keyboard callback? I keep trying but the API returns 0 each time. I need to be able to trap any keys pressed in Windows.... Thanks.
  3. API sub for DoEvents
    I've done a lot of work in VB and am now working on a project that uses a tool called AION, owned by Computer Associates. AION doesn't have a DoEvents function that I've been able to find. But I *can* create a wrapper around Windows API function calls. So the question is, ...
  4. callback messages
    I am programming using an MPEG2 card and am having trouble getting callbacks from it. The first method I tried using the CallBack function kept crashing my program when it was compiled and running outside of the VB environment. The SDK suggestst that with VB you use a CallB...
  5. Async
    I attempted a generic async class, but there are some things I don't like about it. I don't like my delegates with the different args... I'd like to be able to do that cleaner, and I don't really like how I hook up the control to the datasource. I would like that to be clea...

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: Da_WeaselPosted on 2005-10-11 at 18:04:40ID: 15065619

The reason your program gives you the not responding is because the MyCallBack is getting called anywhere from 400-1200 times each time the button is clicked...since VB is single threaded it becomes overwhelmed with the task of processing all of the MyCallBack fucntion calls and giving you what appears to be blocking.  Good old DoEvents saves the day in the same way it always has, it passes control back to the OS and force VB to take a short break, and allow other things happen.

In short your doing it the right way.

Sometimes you may want to limit the number of times you call DoEvents by adding a counter and only calling DoEvents every 10, 1000, 100,000 times.  In this case I don't think limiting the number of times you call DoEvent will improve this in anyway.

As for making use of the info being passed into your MyCallBack fucntion, it will be a little more difficult than simply using the data since its passing your a LPVOID (Long Pointer to a void) which can't be used by VB.  You have to use the MoveMemory and other memory management API calls to copy the data from the structure using the pointer address.  It's not terribly complex to do, but can seem a bit daunting the first few times around.  Here is a link to an excellent web page that addresses this issue and has a nicely designed example program that shows you just about everything you need to get started with WinInet API.

http://vbnet.mvps.org/index.html?http://vbnet.mvps.org/api/_api/apii.htm

 

by: egl1044Posted on 2005-10-11 at 20:57:18ID: 15066042

Hi Da Weasel,

FTPAsync.cls is an ActiveX.DLL marked for apartment-model threading which correct me if I am wrong will handle all the serialization so that a call is never interrupted by a call from another thread.  I am just concerned about why the callback needs to yield control of the processor. There must be another way to handle this correctly.


 

by: egl1044Posted on 2005-10-17 at 19:37:57ID: 15104533

Here is my complete source code, I couldn't get InternetReadFile to work Async But I got everything else to work Async. I suppose its because I am not passing the bytes in chunks rather than passing the entire filesize at once. If possible I need someone to help me implement the InternetReadFile in Async mode. My Current project code below, evertyhing is copy and paste, if you need to see download progress then add progressbar to form. Also if anyone try this project could you let me know of any bugs, I appreciate it


'-----------------------------------------------------
'                   AsyncAPI.BAS
'-----------------------------------------------------

Option Explicit
'===========================================
'           WinInet Async CallBack
'===========================================
'       Author: (Erik L)
'       Init.   (EGL)
'       Email:  egl1044@gmail.com
'
'===========================================
'             FTP ASYNC TYPE
'===========================================

Public Type INERNET_ASYNC_RESULT
    dwResult        As Long
    dwError         As Long
    dwAddress       As Long        'Address Handle created by callback
    dwSize          As Long        'Obtained FileSize from FtpGetFileSize
    dwBytesR        As Long        'Number of received bytes
    dwDownloading   As Boolean
End Type

'===========================================
'             FTP INTERNET ASYNC API
'===========================================
Public Declare Function InternetOpenA Lib "WININET.DLL" ( _
  ByVal lpszAgent As String, _
  ByVal dwAccessType As Long, _
  ByVal lpszProxyName As String, _
  ByVal lpszProxyBypass As String, _
  ByVal dwFlags As Long) As Long

Public Declare Function InternetSetStatusCallback Lib "WININET.DLL" ( _
  ByVal hInternet As Long, _
  ByVal lpfnInternetCallback As Long) As Long
 
Public Declare Function InternetConnectA Lib "WININET.DLL" ( _
  ByVal hConnect As Long, _
  ByVal lpszServerName As String, _
  ByVal nServerPort As Long, _
  ByVal lpszUsername As String, _
  ByVal lpszPassword As String, _
  ByVal dwService As Long, _
  ByVal dwFlags As Long, _
  ByVal dwContext As Long) As Long
 
Public Declare Function InternetReadFile Lib "WININET.DLL" ( _
  ByVal hFile As Long, _
  ByVal sBuffer As Long, _
  ByVal lNumBytesToRead As Long, _
  lNumberOfBytesRead As Long) As Long

Public Declare Function InternetCloseHandle Lib "WININET.DLL" ( _
  ByVal hInternet As Long) As Long

Public mOpen        As Long     '// InternetOpen         Handle
Public mConn        As Long     '// InternetConnect      Handle
Public mBuffer()    As Byte     '// InernetReadFile      Buffer
Public mBytesR      As Long     '// Received Bytes       Array
Public mDownload    As Boolean  '// Downloading          Helper

Public Function RipNulls(ByVal AnyBuffer As String) As String
    RipNulls = Left$(AnyBuffer, InStr(AnyBuffer, vbNullChar) - 1)
End Function



'-----------------------------------------------------
'                   CallBackProc.BAS
'-----------------------------------------------------


Option Explicit

Private Declare Sub BalanceMemoryAny Lib "kernel32" Alias "RtlMoveMemory" ( _
  lpDest As Any, _
  lpSource As Any, _
  ByVal nBytes As Long)

Public Enum InternetStatusVals
  ResolvingName = 10
  NameResolved = 11
  ConnectingToServer = 20
  ConnectedToServer = 21
  SendingRequest = 30
  RequestSent = 31
  ReceivingResponse = 40
  ResponseReceived = 41
  PreFetch = 43
  ClosingConnection = 50
  ConnectionClosed = 51
  HandleCreated = 60
  HandleClosing = 70
  DetectingProxy = 80
  RequestComplete = 100
  Redirecting = 110
  IntermediateResponse = 120
  UserInputRequired = 140
  StateChange = 200
End Enum

Private mIAR As INERNET_ASYNC_RESULT

Public Sub INTERNET_STATUS_CALLBACK( _
    ByVal hInternet As Long, _
    ByVal dwContext As Long, _
    ByVal dwInternetStatus As InternetStatusVals, _
    ByVal lpvStatusInformation As Long, _
    ByVal dwStatusInformationLength As Long)
'InternetStatusCallback:
'   Prototype for an application-defined status callback function.

Dim dwRead As Long
Dim cBuffer As String
Dim Prg As Long

        cBuffer = Space$(dwStatusInformationLength)

    Select Case dwInternetStatus
   
  Case ResolvingName
  'Looking up the IP address of the name contained in lpvStatusInformation
    BalanceMemoryAny ByVal cBuffer, ByVal lpvStatusInformation, dwStatusInformationLength
    Debug.Print RipNulls(cBuffer)

  Case NameResolved
  'Successfully found the IP address of the name contained in lpvStatusInformation.
    BalanceMemoryAny ByVal cBuffer, ByVal lpvStatusInformation, dwStatusInformationLength
    Debug.Print RipNulls(cBuffer)
   
  Case ConnectingToServer
  'Connecting to the socket address (SOCKADDR) pointed to by lpvStatusInformation.
  Debug.Print "Connecting"
 
  Case ConnectedToServer
  'Successfully connected to the socket address (SOCKADDR) pointed to by lpvStatusInformation.
  Debug.Print "Connected"
 
  Case SendingRequest
  'Sending the information request to the server.
  'The lpvStatusInformation parameter is NULL.

  Case RequestSent
  'An asynchronous operation has been completed.
  'The lpvStatusInformation parameter contains the
  'address of an INTERNET_ASYNC_RESULT structure.
  BalanceMemoryAny dwRead, ByVal lpvStatusInformation, dwStatusInformationLength
   
  Case ReceivingResponse
  'Waiting for the server to respond to a request.
  'The lpvStatusInformation parameter is NULL.

  Case ResponseReceived
  'Successfully received a response from the server.
  'The lpvStatusInformation parameter points to a DWORD
  'value that contains the number, in bytes, received.
        Dim Success As Boolean
           
        If mBytesR > 4095 Then
            Success = SetEvent(hEvent)
                BalanceMemoryAny dwRead, ByVal lpvStatusInformation, dwStatusInformationLength
                    mBytesR = mBytesR + dwRead
                        mBytesR = mBytesR
                           
                            'Total bytes transfered
                            With mIAR
                                .dwBytesR = mBytesR - dwRead
                            End With
                           
                        'Used to indicate a download
                        'otherwise don't initiate the
                        'progress bar.
                        If mDownload = True Then
                           Form1.ProgressBar1.Value = mIAR.dwBytesR
                          End If

        Else
            BalanceMemoryAny dwRead, ByVal lpvStatusInformation, dwStatusInformationLength
                    mBytesR = dwRead
                       
                End If
               
  Case ClosingConnection
  'Closing the connection to the server.
  'The lpvStatusInformation parameter is NULL.
  Debug.Print "Closing Connection"
 
  Case ConnectionClosed
  'Successfully closed the connection to the server.
  'The lpvStatusInformation parameter is NULL.
  Debug.Print "Closed Connection"
 
  Case HandleCreated
    'Used by InternetConnect to indicate it has created the new handle.
    'This lets the application call InternetCloseHandle from another thread,
    'if the connect is taking too long. The lpvStatusInformation parameter
    'contains the address of an INTERNET_ASYNC_RESULT structure.
    BalanceMemoryAny mIAR.dwAddress, ByVal lpvStatusInformation, dwStatusInformationLength
   
  Case HandleClosing
  'This handle value has been terminated.
 
  Case RequestComplete
  'An asynchronous operation has been completed.
  'The lpvStatusInformation parameter contains the
  'address of an INTERNET_ASYNC_RESULT structure.
 
  Case Else
    End Select
        DoEvents
   
End Sub

Public Function ReturnAddress() As Long
    'This function holds the Address structure
    'which can be passed back to the application.
    ReturnAddress = mIAR.dwAddress
End Function


'-----------------------------------------------------
'                   IOPending.BAS
'-----------------------------------------------------

Option Explicit

Private Const WAIT_OBJECT_0& = 0
Private Const INFINITE = &HFFFF

Private Const QS_HOTKEY& = &H80
Private Const QS_KEY& = &H1
Private Const QS_MOUSEBUTTON& = &H4
Private Const QS_MOUSEMOVE& = &H2
Private Const QS_PAINT& = &H20
Private Const QS_POSTMESSAGE& = &H8
Private Const QS_SENDMESSAGE& = &H40
Private Const QS_TIMER& = &H10
Private Const QS_MOUSE& = (QS_MOUSEMOVE _
                            Or QS_MOUSEBUTTON)
Private Const QS_INPUT& = (QS_MOUSE _
                            Or QS_KEY)
Private Const QS_ALLEVENTS& = (QS_INPUT _
                            Or QS_POSTMESSAGE _
                            Or QS_TIMER _
                            Or QS_PAINT _
                            Or QS_HOTKEY)

Private Const QS_ALLINPUT& = (QS_SENDMESSAGE _
                            Or QS_PAINT _
                            Or QS_TIMER _
                            Or QS_POSTMESSAGE _
                            Or QS_MOUSEBUTTON _
                            Or QS_MOUSEMOVE _
                            Or QS_HOTKEY _
                            Or QS_KEY)

       
Private Declare Function MsgWaitForMultipleObjects Lib "user32" ( _
    ByVal nCount As Long, _
    pHandles As Long, _
    ByVal fWaitAll As Long, _
    ByVal dwMilliseconds As Long, _
    ByVal dwWakeMask As Long) As Long
   
Private Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" ( _
    lpEventAttributes As Any, _
    ByVal bManualReset As Long, _
    ByVal bInitialState As Long, _
    ByVal lpName As String) As Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function ResetEvent Lib "kernel32" (ByVal hEvent As Long) As Long
Public Declare Function SetEvent Lib "kernel32" (ByVal hEvent As Long) As Long

Public hEvent As Long

Public Function Async_IO_Pending() As Boolean
    'This is my attempt at IO_Pend function
    'the function is used to wait until the
    'callback function is ready for available
    'data
    Dim Success As Boolean
    Dim lBusy As Long
   
    hEvent = CreateEvent(ByVal 0&, Abs(False), Abs(True), vbNullString)
    Success = ResetEvent(hEvent)

    Do
    lBusy = MsgWaitForMultipleObjects(1, hEvent, False, INFINITE, QS_ALLINPUT&)
    DoEvents
    Loop Until lBusy = WAIT_OBJECT_0
   
    mBytesR = 0
    hEvent = CloseHandle(hEvent)
    hEvent = 0
     
End Function



'-----------------------------------------------------
'                  WinInetAsync.CLS
'-----------------------------------------------------



Option Explicit
Option Base 0

'===========================================
'             FTP ASYNC CONST
'===========================================
    Private Const FTP_RELOADS& = &H80000000
    Private Const FTP_PASSIVE& = &H8000000
    Private Const FTP_SERVICE& = 1
    Private Const FTP_PORTNUM& = 21
    Private Const FTP_DIRECT& = 1
    Private Const FTP_READ& = &H80000000
    Private Const FTP_ASYNC& = &H1
   
    Private Const DW_CONTEXT& = 2
    Private Const INVALID_CALLBACK& = -1
    Private Const MAX_PATH As String = 260
   
'===========================================
'             FTP ASYNC API
'===========================================
Private Declare Function FtpGetFile Lib "WININET.DLL" Alias "FtpGetFileA" ( _
  ByVal hConnect As Long, _
  ByVal lpszRemoteFile As String, _
  ByVal lpszNewFile As String, _
  ByVal fFailIfExists As Long, _
  ByVal dwFlagsAndAttributes As Long, _
  ByVal dwFlags As Long, _
  ByVal dwContext As Long) As Long
 
Private Declare Function FtpPutFileA Lib "WININET.DLL" ( _
  ByVal hConnect As Long, _
  ByVal lpszLocalFile As String, _
  ByVal lpszNewRemoteFile As String, _
  ByVal dwFlags As Long, _
  ByVal dwContext As Long) As Long

Private Declare Function FtpSetCurrentDirectoryA Lib "WININET.DLL" ( _
  ByVal hConnect As Long, _
  ByVal lpszDirectory As String) As Long

Private Declare Function FtpGetCurrentDirectoryA Lib "WININET.DLL" ( _
  ByVal hConnect As Long, _
  ByVal lpszCurrentDirectory As String, _
  ByRef lpdwCurrentDirectory As Long) As Long

Private Declare Function FtpOpenFile Lib "WININET.DLL" Alias "FtpOpenFileA" ( _
  ByVal hConnect As Long, _
  ByVal lpszFileName As String, _
  ByVal dwAccess As Long, _
  ByVal dwFlags As Long, _
  ByVal dwContext As Long) As Long
 
Private Declare Function FtpGetFileSize Lib "WININET.DLL" ( _
  ByVal hFile As Long, _
  ByRef lpdwFileSizeHigh As Long) As Long

Private Declare Function FtpDeleteFileA Lib "WININET.DLL" ( _
  ByVal hConnect As Long, _
  ByVal lpszFileName As String) As Long

Private Declare Function FtpCreateDirectoryA Lib "WININET.DLL" ( _
  ByVal hConnect As Long, _
  ByVal lpszDirectory As String) As Long

Private Declare Function FtpRemoveDirectory Lib "WININET.DLL" Alias "FtpRemoveDirectoryA" ( _
  ByVal hFtpSession As Long, _
  ByVal lpszDirectory As String) As Long

Private Declare Function FtpRenameFileA Lib "WININET.DLL" ( _
  ByVal hConnect As Long, _
  ByVal lpszExisting As String, _
  ByVal lpNewFileName As String) As Long

'===========================================
'         FTP ASYNC TRANSFER ENUM
'===========================================
Public Enum FtpTransferTypes
    FTP_TRANSFER_TYPE_UNKNOWN = &H0
    FTP_TRANSFER_TYPE_ASCII = &H1
    FTP_TRANSFER_TYPE_BINARY = &H2
End Enum

    Public Server$         '// Ftp Servername
    Public UserName$       '// Ftp Username
    Public Password$       '// Ftp Password
   
    Public AmIRegistered$
   
    Private m_AsyncResult As INERNET_ASYNC_RESULT
   
Public Sub Connect(Optional AsyncMode As Boolean = True)

    '   AsynchronousMode, otherwise the call is blocked
    '   and marked for Synchronous operations.
   
        '   NOTE: If AsyncMode is False, no CallBack status
        '       will be displayed upon ftp requests
       
    Dim Result As Long
    'InternetOpen:
    '   Initializes an application's use of the WinINet functions.
    mOpen = InternetOpenA( _
                    App.ProductName, _
                    FTP_DIRECT, _
                    vbNullString, _
                    vbNullString, FTP_ASYNC&)
   
    If AsyncMode = True Then
    'InternetSetStatusCallback:
    '   Sets up a callback function that WinINet functions can call as progress is made during an operation.
    Result = InternetSetStatusCallback(mOpen, AddressOf INTERNET_STATUS_CALLBACK)
    End If
   
    'InternetConnect:
    '   Opens an File Transfer Protocol (FTP), Gopher, or HTTP session for a given site.
    mConn = InternetConnectA( _
                    mOpen, _
                    Server, _
                    FTP_PORTNUM&, _
                    UserName, _
                    Password, _
                    FTP_SERVICE&, _
                    FTP_PASSIVE&, DW_CONTEXT&)
                   
                    Async_IO_Pending
End Sub

Public Function Disconnect() As Boolean
   
'   If asynchronous requests are pending for
'   the handle or any of its child handles
'   the handle cannot be closed immediately
'   but it will be invalidated.
   
    'Clean up
    Dim Success As Boolean
        Success = SetEvent(hEvent)
    'InternetCloseHandle
    '   Closes a single Internet handle.
    Call InternetCloseHandle(mConn)
    Call InternetCloseHandle(mOpen)
    'Close the Address Handle, this handle
    'is the same as the handle returned
    'from InternetopenA
    Call InternetCloseHandle(ReturnAddress)
   
     mConn = 0
     mOpen = 0
     
End Function

Public Function FtpDownload(ByVal RemoteFile As String, _
                    ByVal LocalFile As String, _
                    ByVal TransferMode As FtpTransferTypes, _
                    Optional ProgressB As Object) As Boolean
   
    'Let callback know we are downloading
    'and set up the progressbar status.
    mDownload = True
   
    Call FtpReadFile(RemoteFile, TransferMode)
    ProgressB.Max = m_AsyncResult.dwSize
   
    'FtpGetFile:
    '   Retrieves a file from the FTP serverand stores
    '   it under the specified file name, creating a
    '   new local file in the process.
    Dim Success As Boolean
        Success = FtpGetFile(mConn, RemoteFile, LocalFile, False, ByVal 0&, TransferMode, DW_CONTEXT&)
            FtpDownload = Success
End Function

Public Function FtpUpload(ByVal RemoteFile As String, _
                    ByVal LocalFile As String, _
                    ByVal TransferMode As FtpTransferTypes) As Boolean
                   
    'FtpPutFile:
    '   Stores a file on the FTP server
    Dim Success As Boolean
        Success = FtpPutFileA(mConn, LocalFile, RemoteFile, TransferMode, DW_CONTEXT&)
            FtpUpload = Success
End Function

Public Function FtpGetDirectory() As String
'FtpGetCurrentDirectory:
    '   Retrieves the current directory for the specified FTP session.
    Dim DirBuff As String
    Dim strTemp As String
    Dim Success As Boolean
   
    DirBuff = String$(MAX_PATH, vbNullChar)
        Success = FtpGetCurrentDirectoryA(mConn, DirBuff, Len(DirBuff))
            FtpGetDirectory = RipNulls(DirBuff)
End Function

Private Function FtpReadFile(ByVal FtpFileName As String, _
                            ByVal TransferMode As FtpTransferTypes) As Boolean
   
    Dim lHandle As Long
    Dim llowSize As Long
    Dim lHighSize As Long
    Dim lOpen As Long
    Dim lConn As Long
    Dim Dummy As Long
    Dim Result As Long
   
    '   After calling FtpOpenFile and until calling
    '   InternetCloseHandle, all other calls to FTP
    '   functions on the same FTP session handle
    '   will fail.
   
    '   Objective:  Initialize a new temporary session handle
    '               used only to obtain the information
    '               then close all handles immediately.
       
        lOpen = InternetOpenA( _
                    "Temp", _
                    FTP_DIRECT, _
                    vbNullString, _
                    vbNullString, FTP_ASYNC&)
                   
        Result = InternetSetStatusCallback(mOpen, AddressOf INTERNET_STATUS_CALLBACK)
       
        lConn = InternetConnectA( _
                    lOpen, _
                    Server, _
                    FTP_PORTNUM&, _
                    UserName, _
                    Password, _
                    FTP_SERVICE&, _
                    FTP_PASSIVE&, DW_CONTEXT&)
                   
            'FtpOpenFile:
            '   Initiates access to a remote file on an
            '   FTP server for reading or writing.
        lHandle = FtpOpenFile(lConn, FtpFileName, FTP_READ, TransferMode, DW_CONTEXT&)
            'FtpGetFileSize:
            '   Retrieves the file size of the requested FTP resource.
        llowSize = FtpGetFileSize(lHandle, lHighSize)
           
            'Add the filesize to the structure
            m_AsyncResult.dwSize = llowSize
           
            ' Close the temporary handles
            lHandle = InternetCloseHandle(lConn)
            lHandle = InternetCloseHandle(lOpen)
            lConn = 0
            lOpen = 0
End Function

Public Function InternetFileReadDownload(ByVal sFileName As String, ByVal sOutputFile As String, _
                                 ByVal TransferMode As FtpTransferTypes)
       
        'I couldn't get InternetReadFile to work
        'async, however FtpGetFile, and FtpPutFile
        'work async below demonstrates how to read
        'entire file into a byte array for output.

        Dim lHandle As Long
        Dim lFree As Long
       
        'Get the filesize in bytes
        Call FtpReadFile(sFileName, TransferMode)
        'Debug.Print m_AsyncResult.dwsize

ReDim mBuffer(0 To m_AsyncResult.dwSize)

    'FtpOpenFile:
    '   Initiates access to a remote file on an FTP server for reading or writing.
   
    lHandle = FtpOpenFile(mConn, sFileName, FTP_READ, TransferMode Or FTP_RELOADS&, DW_CONTEXT&)
    'InternetReadFile:
    '   Reads data from a handle opened by the
    '   InternetOpenUrl, FtpOpenFile, GopherOpenFile,
    '   or HttpOpenRequest function.

        If (lHandle) > 0 Then
    ' Read the entire file
    InternetReadFile lHandle, _
            ByVal VarPtr(mBuffer(0)), _
            ByVal m_AsyncResult.dwSize, _
            ByVal VarPtr(mBuffer(m_AsyncResult.dwSize))
           
                Else
            Debug.Print "FtpOpenFile Error"
            lHandle = InternetCloseHandle(lHandle)
                Exit Function
            End If
       
        'Output the file to hard disk
        lFree = FreeFile
        Open sOutputFile For Binary As #lFree
            Put #lFree, , mBuffer
        Close #lFree

        'Reset byte size
        Erase mBuffer()
           'Reset structure size
           m_AsyncResult.dwSize = 0
            lHandle = 0
       
End Function

Friend Function FtpSetDirectory(ByVal SSetDir As String) As Boolean
    'FtpSetCurrentDirectory:
    '   Changes to a different working directory on the FTP server.
    Dim Success As Boolean
        Success = FtpSetCurrentDirectoryA(mConn, SSetDir)
            FtpSetDirectory = Success
End Function

Friend Function FtpFileDelete(ByVal sFileName As String) As Boolean
    'FtpDeleteFile:
    '   Deletes a file stored on the FTP server.
    Dim Success As Boolean
        Success = FtpDeleteFileA(mConn, sFileName)
            FtpFileDelete = Success
End Function

Friend Function FtpFileRename(ByVal ExistingFileName As String, _
                                ByVal RenameFile As String) As Boolean
'FtpRemoveDirectory:
'   Removes the specified directory on the FTP server.
    Dim Success As Boolean
        Success = FtpRenameFileA(mConn, ExistingFileName, RenameFile)
            FtpFileRename = Success
End Function

Friend Function FtpDirectoryCreate(ByVal CreateNewDirName As String) As Boolean
'FtpCreateDirectory:
'   Creates a new directory on the FTP server.
    Dim Success As Boolean
        Success = FtpCreateDirectoryA(mConn, CreateNewDirName)
            FtpDirectoryCreate = Success
End Function

Friend Function FtpDirectoryRemove(ByVal RemoveDirectoryName As String) As Boolean
'FtpRemoveDirectory
'   Removes the specified directory on the FTP server.
    Dim Success As Boolean
        Success = FtpRemoveDirectory(mConn, RemoveDirectoryName)
            FtpDirectoryRemove = Success
End Function




'-----------------------------------------------------
'                   Form Code Example
'-----------------------------------------------------

'Add 3 Command Buttons, default names


Option Explicit

Dim w As WinInetAsync
   
Private Sub Command1_Click()
    'This performs sync
    'w.InternetFileReadDownload "calc.exe", "D:\calc.exe", FTP_TRANSFER_TYPE_BINARY
   
    'This performs async with progress display
    w.FtpDownload "calc.exe", "D:\calc.exe", FTP_TRANSFER_TYPE_BINARY, ProgressBar1
End Sub

Private Sub Command2_Click()
    w.Server = "ftp.server.com"
    w.UserName = "user id"
    w.Password = "password"
    w.Connect True
End Sub

Private Sub Command3_Click()
    w.Disconnect
End Sub

Private Sub Form_Load()
Set w = New WinInetAsync

Command1.Caption = "Download"
Command2.Caption = "Connect"
Command3.Caption = "Disconnect"
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If ReturnAddress > 0 Then w.Disconnect
    Set w = Nothing
End Sub

 

by: EDDYKTPosted on 2005-10-18 at 05:06:05ID: 15106492

 

by: egl1044Posted on 2005-10-18 at 05:32:00ID: 15106661

Hi EDDYKT, Thats a good resource but it doesn't use InternetReadFile, if possible I need to read 4096 chunks at a time and just run it in a loop, but I have trouble shifting the bytes correctly. I can't use a string buffer so I need to use a byte array with varptr because my decleration of InternetReadFile needs to be Long() in order to be passed into the callback function.

 

by: EDDYKTPosted on 2005-10-18 at 06:00:19ID: 15106897

 

by: egl1044Posted on 2005-10-18 at 07:55:01ID: 15108080

That link might help a little, I was just looking for a strait forward example on how to shift the bytes.

And example is something like this...For instance say the file is 114688 bytes which is the size of Calc.exe which is = 112 KB but we will only use the bytes.

First I pull the total bytes of the file from the ftp which I can do with no problems, The problem I am having is that I can get it to work if I specify the entire length of the file like this...

Dim FileSize as Long  'Long pointer
Dim mBuffer() as Byte 'This is the byte array

'Which holds a buffer for the file..
ReDim mBuffer(0 To FileSize)

'Internet ReadFile will now use the buffer from the byte array and download the file
InternetReadFile lHandle, _
            ByVal VarPtr(mBuffer(0)), _
            ByVal FileSize, _
            ByVal VarPtr(mBuffer(FileSize))


The problem is I want to read in chunks, say 4096 bytes each loop until it reaches the size of the file.

 

by: egl1044Posted on 2005-10-19 at 09:46:36ID: 15117574

Hey EDDYKT, I figured it out, the points are yours, could you test this code for me the below is just an HTTP download transfer

'--------------  Module1.BAS --------------


Option Explicit

Private Declare Function InternetOpenA Lib "WININET.DLL" ( _
  ByVal lpszAgent As String, _
  ByVal dwAccessType As Long, _
  ByVal lpszProxyName As String, _
  ByVal lpszProxyBypass As String, _
  ByVal dwFlags As Long) As Long

Private Declare Function InternetOpenUrl Lib "WININET.DLL" Alias "InternetOpenUrlA" ( _
  ByVal hInternet As Long, _
  ByVal lpszUrl As String, _
  lpszHeaders As Any, _
  ByVal dwHeadersLength As Long, _
  ByVal dwFlags As Long, _
  ByVal dwContext As Long) As Long
 
Private Declare Function InternetReadFile Lib "WININET.DLL" ( _
  ByVal hFile As Long, _
  ByVal sBuffer As Long, _
  ByVal lNumBytesToRead As Long, _
  lNumberOfBytesRead As Long) As Long

Private Declare Function InternetCloseHandle Lib "WININET.DLL" ( _
  ByVal hInternet As Long) As Long

Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" ( _
  ByVal lpFileName As String, _
  ByVal dwDesiredAccess As Long, _
  ByVal dwShareMode As Long, _
  ByVal lpSecurityAttributes As Long, _
  ByVal dwCreationDisposition As Long, _
  ByVal dwFlagsAndAttributes As Long, _
  ByVal hTemplateFile As Long) As Long
 
Private Declare Function WriteFile Lib "kernel32" ( _
  ByVal hFile As Long, lpBuffer As Any, _
  ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, _
  ByVal lpOverlapped As Any) As Long
 
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Public Function Download_File(ByVal FileName As String, _
                                ByVal WebURL As String)


Dim hLocalFile As Long
Dim buffer() As Byte
Dim bytesRead As Long
Dim bytesWritten As Long
Dim bytesTransferred As Long
Dim boolCancel As Boolean
Dim mOpen As Long
Dim lHandle As Long

mOpen = InternetOpenA(App.ProductName, 1, vbNullString, vbNullString, &H1)
lHandle = InternetOpenUrl(mOpen, WebURL, vbNullString, ByVal 0&, &H2, 2)
hLocalFile = CreateFile(FileName, &H40000000, &H2, ByVal 0&, 2, 0, 0)
 
 If hLocalFile <> 0 Then
        'Read 8 KB Chunks
        Const bufferLen As Long = 8192
        ReDim buffer(8192)
       
        Do
        If InternetReadFile(lHandle, ByVal VarPtr(buffer(0)), bufferLen, bytesRead) Then
            If WriteFile(hLocalFile, ByVal VarPtr(buffer(0)), bytesRead, bytesWritten, ByVal 0&) Then
                bytesTransferred = bytesTransferred + bytesWritten
                    Debug.Print bytesTransferred 'bytesTransferred
            End If
        Else
            boolCancel = True
        End If
            DoEvents
    Loop While bytesRead = bufferLen And Not boolCancel
         
    End If

    Call CloseHandle(hLocalFile)
    Call InternetCloseHandle(lHandle)
    Call InternetCloseHandle(mOpen)

End Function


'-------------- Form1 --------------


Private Sub Command1_Click()
Call Download_File("D:\vbrun60.exe", _
        "http://download.microsoft.com/download/vb60pro/install/6/win98me/en-us/vbrun60.exe")
End Sub

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...