views:

659

answers:

2

Hi,

I'm trying to use Winsock to download some files and save them. In my case, I have a MSHFlexGrid with 2 columns: one with URL and the other with the "path+filename" (where the file is going to be saved). I'm iterating through all rows calling the next function:

Public Function DownloadSock(ArqURL As String, ArqDestino As String) As Boolean
'ArqURL is the file URL
'ArqDestino is where the downloaded file is going to be stored, in my hard disc

Dim arquivo() As Byte
Dim ficheiroID As Integer

ficheiroID = FreeFile
On Error GoTo Trata_erro
Open ArqDestino For Binary Access Write As #ficheiroID


Me.Winsock1.Connect ArqURL, 80 
Me.Winsock1.GetData arquivo()
Put #ficheiroID, , arquivo()

Close #ficheiroID

DownloadSock = True


Exit Function

Trata_erro:

    MDIForm1.Text1 = MDIForm1.Text1 & "Error! " & Err.Number & Err.Description & " - " & Err.Source & " - URL: " & ArqURL & " - Destino: " & ArqDestino & vbNewLine
    DownloadSock = False

End Function

I'm getting this error

40006: Wrong protocol or connection state for the requested transaction or request

What am I doing wrong?

Thanks

+2  A: 

Have you checked out this Microsoft Support page? It indicates there's a bug in the Winsock control and the hotfix may be helpful.

Another thing to try is to make sure your winsock connection is open before trying to read/send data, and if it is closed, reopen a new connection:

if winsock.state=9 ' error state
  winsock.close
  while winsock.state<>0 ' closed state
    doEvents
  wend ' you need a while loop, because it doesn't close "immediately".
end if
' now you reopen it, or do whatever else you need

You might also consider changing your connection code to something like:

With Winsock1
      If .State <> sckClosed Then .Close
      .RemoteHost = ArqURL 
      .RemotePort = 80
      .Connect
End With

One last thing. Check out this post on using the Winsock control.

C-Pound Guru
+1 - Hotfix or no hotfix, you have to wait for the connection to be opened before you can do anything with it. That behavior is by design. As an aside, to avoid using `DoEvents` (which is pretty evil in VB6 due to the nasty re-entrancy issues it can cause), I would prefer to create a separate `FileDownloader` class with a `BeginDownload` method that takes a URL and a `WinSock` instance as parameters. It opens the connection, and then starts the download when the `WinSock` `Connect` event fires. Then it could raise a `DownloadCompleted` event when all the data is received.
Mike Spross
This approach would keep everything asynchronous while avoiding the need to call `DoEvents` waiting for `Winsock.State` to change. I'm particularly wary of `DoEvents` after dealing with way too many hard-to-debug issues caused by code that used it, so I tend to avoid it at all costs, even when it seems like the easy way to do implement a busy-wait loop.
Mike Spross
+1  A: 

I think you have overestimated the power of the Winsock control. You can't just use the Winsock's GetData method to reach out and grab a file. There has to be an active connection between your client application and some other application on the server side. After a connection is established, this server application will feed data to your application, the Winsock's DataArrival event will fire, and then you can use the GetData method to retrieve it. Your code should look more like this:

Public Sub DownloadSock(ArqURL As String)

  Dim arquivo() As Byte
  Dim ficheiroID As Integer

  ficheiroID = FreeFile
  Me.Winsock1.Connect ArqURL, 80

End Function

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)

  Dim ArqDestino As String
  Dim arquivo() As Byte
  Dim ficheiroID As Integer

  ficheiroID = FreeFile
  Open ArqDestino For Binary Access Write As #ficheiroID
  Me.Winsock1.GetData arquivo()
  Put #ficheiroID, , arquivo()
  Close #ficheiroID

End Sub

This is far from complete however (nor is it guaranteed to be syntactically correct, consider it pseudo code). After making the connection, you then have to implement some mechanism to tell the server to begin sending the file. If the file is large enough it will take many DataArrival events to get it all, so it will have to be held in an accumulator while the data comes across. There's more to this than you think.

I would take a look at some tutorials and/or sample code (look for a VB6 project that uses the Winsock control on CodeProject, like this one).

raven