4

Today I was asked for help with a FoxPro issue, about how to download a file via HTTP.

I found two things: one was a paid ActiveX, and the other one requires libcurl.

Is there a way to do that without anything additional (VFP 8), something like HttpURLConnection in Java? For example by using Microsoft.XMLHTTP

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140

6 Answers6

8

Two snippets that work, and require no additional files/dlls/flls/etc.

Local loRequest, lcUrl, lcFilename

lcUrl = "http://example.com/foo.zip"
lcFilename = "C:\Temp\PSV.zip"

loRequest = Createobject('MsXml2.XmlHttp')
loRequest.Open("GET",lcUrl,.F.)
loRequest.Send()
StrToFile(loRequest.ResponseBody,lcFilename)

and

lox = CREATEOBJECT("inetctls.inet")
lcSuff = lox.OpenURL("http://whatever.co.uk/suff.htm")
STRTOFILE(lcStuff, "c:\data\myfile.htm")

(taken from here)

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
1

Is a my HttpClient.prg file (only GET response supported):

#define INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY 4

#define REQ_STATE_UNINITIALIZED 0 && open()has not been called yet.
#define REQ_STATE_LOADING       1 && send()has not been called yet.
#define REQ_STATE_LOADED        2 && send() has been called, and headers and status are available.
#define REQ_STATE_INTERACTIVE   3 && Downloading; responseText holds partial data.
#define REQ_STATE_COMPLETED     4 && The operation is complete.

DEFINE CLASS HttpClientRequest As Custom
  readystate=REQ_STATE_UNINITIALIZED
  Protocol=NULL
  Url=NULL
  requestBody=NULL
  responseBody=NULL

  PROCEDURE Open(tcProtocol, tcUrl)
    IF this.readystate != REQ_STATE_UNINITIALIZED
      ERROR "HttpClientRequest is already opened."
    ENDIF
    IF VARTYPE(m.tcProtocol)!="C" OR VARTYPE(m.tcUrl)!="C" 
      ERROR "Invalid type or count of parameters."
    ENDIF
    IF NOT INLIST(m.tcProtocol,"GET")
      ERROR "Unsupported or currently not implemented protocol type."
    ENDIF
    this.Protocol = m.tcProtocol
    this.Url = m.tcUrl
    this.readystate = REQ_STATE_LOADING
  ENDPROC

  PROCEDURE Send(tcBody)
    IF this.readystate != REQ_STATE_LOADING
      ERROR "HttpClientRequest is not in initialized state."
    ENDIF
    IF PCOUNT()=0
      m.tcBody=NULL
    ENDIF
    IF this.Protocol=="GET" AND (NOT ISNULL(m.tcBody))
      ERROR "Invalid type or count of parameters."
    ENDIF
    this.requestBody = m.tcBody
    this.readystate = REQ_STATE_LOADED


    DECLARE integer InternetOpen IN "wininet.dll" ;
      string @ lpszAgent, ;
      integer dwAccessType, ;
      string @ lpszProxyName, ;
      string @ lpszProxyBypass, ;
      integer dwFlags
    DECLARE integer InternetCloseHandle IN "wininet.dll" ;
      integer hInternet
    DECLARE integer InternetCanonicalizeUrl IN "wininet.dll" ;
      string @ lpszUrl, ;
      string @ lpszBuffer, ;
      integer @ lpdwBufferLength, ;
      integer dwFlags
    DECLARE integer InternetOpenUrl IN "wininet.dll" ;
      integer hInternet, ;
      string @ lpszUrl, ;
      string @ lpszHeaders, ;
      integer dwHeadersLength, ;
      integer dwFlags, ;
      integer dwContext
    DECLARE integer InternetReadFile IN "wininet.dll" ;
      integer hFile, ;
      string @ lpBuffer, ;
      integer dwNumberOfBytesToRead, ;
      integer @ lpdwNumberOfBytesRead

    LOCAL m.hInternet,lcUrl,lnUrlLen,m.hInternetFile,lcBuffer,lnBufferLen,lnReaded
    m.hInternet = InternetOpen("a.k.d. HttpClientRequest for Visual FoxPro", ;
                               INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, ;
                               NULL, NULL, 0)
    this.responseBody = ""
    IF m.hInternet != 0
      m.lnUrlLen = LEN(this.Url)*8
      m.lcUrl = REPLICATE(CHR(0),m.lnUrlLen)
      InternetCanonicalizeUrl(this.Url, @lcUrl, @lnUrlLen, 0)
      m.hInternetFile = InternetOpenUrl(m.hInternet, @lcUrl, NULL, -1, 0, 0)
      IF m.hInternetFile != 0
        m.lnBufferLen = 10240
        DO WHILE .T.
           m.lcBuffer = REPLICATE(CHR(0),m.lnBufferLen)
           m.lnReaded = 0
           IF NOT (0!=InternetReadFile(m.hInternetFile, @lcBuffer, m.lnBufferLen, @lnReaded) AND m.lnReaded>0)
             EXIT
           ENDIF
          this.responseBody = this.responseBody + LEFT(m.lcBuffer,m.lnReaded)
          this.readystate = REQ_STATE_INTERACTIVE
        ENDDO
        InternetCloseHandle(m.hInternetFile)
      ENDIF
      InternetCloseHandle(m.hInternet)
    ENDIF
    this.readystate = REQ_STATE_COMPLETED
  ENDPROC
ENDDEFINE

Usage for GET requests:

Local HttpClient
m.HttpClient = NEWOBJECT("HttpClientRequest","httpclient.prg")
m.HttpClient.Open("GET","http://servername/path/resourcename")
m.HttpClient.Send()

After above code is executed server response contained in m.HttpClient.responseBody property and you can store value into file or for example for pictures into PictureVal property of Image object:

STRTOFILE(m.HttpClient.responseBody,"c:\filename");

m.myform.AddObject("myimg",""image")
m.myform.myimg.PictureVal=m.HttpClient.responseBody
Andrew D.
  • 8,130
  • 3
  • 21
  • 23
0

Look into using West Wind Web Connect. This is a framework that will allow you to write VFP application that are accessible from the web.

Jerry
  • 6,357
  • 8
  • 35
  • 50
0

You can do it in VFP, but it requires registering the Windows DLLs to open connection handles, and make calls to get data.

Another option is to use automation, such as with Internet Explorer. You can create an ie object from within VFP, and invoke its methods to open a given URL, wait until it's "Ready State" is complete, then look at the content. As for trying to get things that require URL parameter strings, you can add those no problem, but if it requires POST variables, that is a little more effort.

As mentioned by Jerry, the West-Wind tools are quite powerful and Rick Strahl has been doing it since... about 1993 that I remember. His other tool is specifically wwIPTools.DLL that offers even more features.

DRapp
  • 47,638
  • 12
  • 72
  • 142
0

You could also look at Craig Boyd's free VFPConnection library, he also has an excellent free JSON library.

Alan B
  • 4,086
  • 24
  • 33
  • that's the link I pasted in my original question. Alas, the newest version doesn't work. – Bozho Dec 16 '11 at 09:34
0

Option 1:

Declare Integer URLDownloadToFile In urlmon.dll As _apiURLDownloadToFile;
Integer pCaller, ;
String  szURL, ;
String  szFileName, ;
Integer dwReserved, ;
Integer lpfnCB

Just make shure to clear the file from the cache first:

Declare Integer DeleteUrlCacheEntry In wininet.dll As _apiDeleteUrlCacheEntry ;
    String  lpszUrlName

Or add a random parameter at the end of the url, like "?somerandomvalue" for example.

Option 2:

Declare Integer InternetOpen In wininet.dll As _apiInternetOpen ;
    String  lpszAgent, ;
    Integer dwAccessType, ;
    String  lpszProxy, ;
    String  lpszProxyBypass, ;
    Integer dwFlags

Declare Integer InternetOpenUrl In wininet.dll As _apiInternetOpenUrl ;
    Integer hInternet,;
    String  lpszUrl,;
    String  lpszHeaders,;
    Integer dwHeadersLength,;
    Integer dwFlags,;
    Integer dwContext

Declare Integer InternetReadFile In wininet.dll As _apiInternetReadFile ;
    Integer hFile, ;
    String  @lpBuffer, ;
    Integer dwNumberOfBytesToRead, ;
    Integer @lpdwNumberOfBytesRead

Declare Integer InternetCloseHandle In wininet.dll As _apiInternetCloseHandle ;
    Integer hInternet

The proper use of the functions can be found on MSDN.

PS: You missed this one: http://curl.haxx.se/libcurl/foxpro/

Carlos Alloatti
  • 193
  • 2
  • 9