## URL Open Stream Functions

"...pass in a URL, get a stream..."

### Overview

Without a doubt, the easiest, most efficient, and most powerful way to download data from the Internet is to use the new URL Open Stream (UOS) functions. Before jumping into the specifications, let's cover a few of the high-level features and the philosophy behind the creation of these functions. The UOS functions are the newest addition to the ActiveX™ extensions to the Win32 API. They combine the familiarity of C-style programming with the power of COM. These functions work equally well inside an ActiveX framework (for example, a component, a document or frame window, a subcomponent, or a scriptable object) or standalone.

Unlike other more complex interface negotiations, using these functions requires knowledge of no more than two COM interfaces, IStream and IBindStatusCallback.

The UOS functions use services from URL monikers and WinInet; therefore, all the caching and thread synchronization features of those components are in use whenever you call these functions. In addition, these functions handle all the host binding operations if your code is in an ActiveX container: the UOS functions automatically do the right thing to manage the download in the ActiveX client framework.

Every UOS function works in the same basic way: the caller implements an IBindStatusCallback interface (optional in some cases), then calls the function. The URLOpenStream and URLOpenPullStream functions require the caller to be on a thread that has a message loop (GetMessage/DispatchMessage). For ActiveX components, a message loop is given when these functions are called from the main thread. For standalone applications without a user interface, a message loop is still necessary for these functions.

With the URL Open Stream functions, you can:

- Download a URL to a file with a single function call. You can optionally get progress notifications in the background.
- Create a blocking-type stream with a single function that will block when you call IStream::Read. You can optionally get progress notifications in the background.
- Post an Internet query (using the HTTP POST verb) with a single call and get the results in a stream.
- Optionally hook into the ActiveX client framework by passing your this pointer.
- Configure callbacks using either the push or pull model.

#### IBindStatusCallback

Callbacks are issued on an IBindStatusCallback interface that is implemented on the caller. This is a simple callback interface to implement because most of the methods on the interface are optional (for example, OnStartBinding, GetPriority, OnStopBinding) and can return S_OK or E_NOTIMPL. Clients may choose to implement many of the IBindStatusCallback methods; however, only OnDataAvailable is needed for the UOS functions URLOpenStream and URLOpenPullStream to work correctly. In fact, for some UOS functions (URLDownloadToFile, URLDownloadToCacheFile and URLOpenBlockingStream), OnDataAvailable is never called because it is unnecessary. Furthermore, GetBindInfo is never invoked for UOS clients because the the UOS function that is called determines the bind information. The UOS programming model is straightforward because there are no special flags to pass to the functions.

The URL Open Stream functions are described below:

#### URLDownloadToCacheFile

    HRESULT URLDownloadToCacheFile(
        LPUNKNOWN            lpUnkcaller,
        LPCWSTR             szURL,
        LPWSTR              szFileName,
        DWORD               dwBufLength,
        DWORD               dwReserved,
        IBindStatusCallback *pBSC
    );

URLDownloadToCacheFile downloads data into the Internet cache and returns the filename of the cache location for retrieving the bits. The client can choose to be notified of progress via a notification callback.

This function will always return a filename if the download operation succeeds. If the given URL is a "file:" URL, URLDownloadToCacheFile will directly return the filename for the "file:" URL rather than making a copy to the cache. If the given URL is an Internet URL ("http:", "ftp:"), URLDownloadToCacheFile will download this file and return the local filename of the cached copy. Using this function ensures that a filename is returned without unnecessary copying of data.

lpUnkcaller
  Pointer to the controlling IUnknown of the calling ActiveX component (if the caller is an ActiveX component). If the caller is not an ActiveX component, this value may be set to NULL. Otherwise, the caller is a COM object that is contained in another component (such as an ActiveX Control within the context of an HTML page). This parameter represents the outermost IUnknown of the calling component. The function will attempt the download within the context of the ActiveX client framework and allow the caller's container to receive callbacks on the progress of the download.
szURL
  The URL to be downloaded. Cannot be NULL.
szFileName
  The name of the downloaded file. Cannot be NULL.
dwBufLength
  The count of bytes that were downloaded.
dwReserved
  Reserved for future use. Must be zero.
lpfnCB
  Pointer to the caller's IBindStatusCallback interface. URLDownloadToCacheFile calls this interface's OnProgress method on a connection activity, including the arrival of data. OnDataAvailable is never called. Implementing OnProgress allows a caller to implement a user interface or other progress monitoring functionality. It also allows the download operation to be canceled by returning E_ABORT from the OnProgress call. Can be NULL.

#### URLDownloadToFile

    HRESULT URLDownloadToFile(
        LPUNKNOWN pCaller,
        LPCWSTR szURL,
        LPCTSTR szFileName,
        DWORD dwReserved,
        LPBINDSTATUSCALLBACK lpfnCB
    );

URLDownloadToFile downloads bits from the Internet and saves them to a file. The client can choose to be notified of progress via a notification callback.

pCaller
  Pointer to the controlling IUnknown of the calling ActiveX component (if the caller is an ActiveX component). If the caller is not an ActiveX component, this value may be set to NULL. Otherwise, the caller is a COM object that is contained in another component (such as an ActiveX Control within the context of an HTML page). This parameter represents the outermost IUnknown of the calling component. The function will attempt the download within the context of the ActiveX client framework and allow the caller's container to receive callbacks on the progress of the download.
szURL
  The URL to be downloaded. Cannot be NULL.
szFileName
  Name of the file to create for bits that come from the download.
dwReserved
  Reserved for future use. Must be zero.
lpfnCB
  Pointer to the caller's IBindStatusCallback interface. URLDownloadToFile calls this interface's OnProgress method on a connection activity, including the arrival of data. OnDataAvailable is never called. Implementing OnProgress allows a caller to implement a user interface or other progress monitoring functionality. It also allows the download operation to be canceled by returning E_ABORT from the OnProgress call. Can be NULL.

#### URLOpenStream

    HRESULT URLOpenStream(
        LPUNKNOWN pCaller,
        LPCWSTR szURL,
        DWORD dwReserved,
        LPBINDSTATUSCALLBACK lpfnCB
    );

URLOpenStream creates a push-type stream object from a URL. The data is downloaded from the Internet as fast as possible. When data is available, it is "pushed" at the client through a notification callback.

pCaller
  Pointer to the controlling IUnknown of the calling ActiveX component (if the caller is an ActiveX component). If the caller is not an ActiveX component, this value may be set to NULL. Otherwise, the caller is a COM object that is contained in another component (such as an ActiveX Control within the context of an HTML page). This parameter represents the outermost IUnknown of the calling component. The function will attempt the download within the context of the ActiveX client framework and allow the caller's container to receive callbacks on the progress of the download.
szURL
  The URL to be converted to a stream object. Cannot be NULL.
dwReserved
  Reserved for future use. Must be zero.
lpfnCB
  Pointer to the caller's IBindStatusCallback interface, on which URLOpenStream calls OnDataAvailable when data arrives from the Internet. OnDataAvailable can return E_ABORT to abort the download. When the callback is invoked and the pstm member of the STGMEDIUM structure is not NULL, the caller can read from the stream the amount of data specified in the dwSize argument passed with the OnDataAvailable call. If the caller does not read the full amount or does not call pstm Read at all, OnDataAvailable will still be called the next time data arrives if the grfBSCF flags do not indicate BINDF_LASTDATANOTIFICATION. In that case, no more data will be downloaded. Any data that is not read at any given time will still be available the next time OnDataAvailable is called.

The logic in the following code fragment is a typical implementation of OnDataAvailable as it is used by the URLOpenStream function:

    HRESULT MyBindStatusCallback::OnDataAvailable (DWORD grfBSCF,
        DWORD dwSize, ..., STGMEDIUM * pstgmed)
    {

        if( dwSize &lt  sizeof(BITMAPINFOHEADER) )
            return(NOERROR);  // not enough has been read yet, just return

        if( !g_bGotInfoHeader ) {   // did we get info before?
            // No, go ahead, read now...

            DWORD dwRead;
            HRESULT hr = pstgmed->pstm->Read( &bmih, sizeof(bmih), &dwRead);
            if( SUCCEEDED(hr) ) {
                // now we got it... we can return
                g_bGotInfoHeader = TRUE;
                return(hr);
            }
        }
    }

#### URLOpenBlockingStream

    HRESULT URLOpenBlockingStream(
        LPUNKNOWN pCaller,
        LPCWSTR szURL,
        LPSTREAM *ppStream,
        DWORD dwReserved,
        LPBINDSTATUSCALLBACK lpfnCB
    );

URLOpenBlockingStream creates a blocking-type stream object from a URL. The data is downloaded from the Internet on demand by a call to IStream::Read. The Read call will block until enough data has arrived.

pCaller
  Pointer to the controlling IUnknown of the calling ActiveX component (if the caller is an ActiveX component). If the caller is not an ActiveX component, this value may be set to NULL. Otherwise, the caller is a COM object that is contained in another component (such as an ActiveX Control within the context of an HTML page). This parameter represents the outermost IUnknown of the calling component. The function will attempt the download within the context of the ActiveX client framework and allow the caller's container to receive callbacks on the progress of the download.
szURL
  The URL to be converted to a stream object. Cannot be NULL.
ppStream
  Pointer to the IStream interface on the stream object created by this function. The caller can read from the stream as soon as it has this pointer. If the data requested has not yet been downloaded, the Read method will block until enough data has been downloaded. The following is a code fragment that logically does this:

          IStream * pStream;
          URLOpenStream( 0, L"http://www.msn.com/", &pStream, 0, 0);

          char buffer[0x100];

          DWORD dwGot;
          HRESULT hr = NOERROR;

          do {
              hr = pStream->Read( buffer, sizeof(buffer), &dwGot );

              // do something with contents of buffer

          } while( SUCCEEDED(hr) );

dwReserved
  Reserved for future use. Must be zero.
lpfnCB
  Pointer to the caller's IBindStatusCallback interface. URLOpenBlockingStream calls this interface's OnProgress method on a connection activity, including the arrival of data. OnDataAvailable is never called. Implementing OnProgress allows a caller to implement a user interface or other progress monitoring functionality. It also allows the download operation to be canceled by returning E_ABORT from the OnProgress call. Can be NULL.

#### URLOpenPullStream

    HRESULT URLOpenPullStream(
        LPUNKNOWN pCaller,
        LPCWSTR szURL,
        DWORD dwReserved,
        LPBINDSTATUSCALLBACK lpfnCB
    );

URLOpenPullStream creates a pull–type stream object from a URL. The data is downloaded from the Internet on demand. If not enough data is available locally to satisfy the requests, the IStream::Read call will not block until enough data has arrived. Instead, Read will immediately return E_PENDING, and URLOpenPullStream will request the next packet of data from the Internet server.

pCaller
  Pointer to the controlling IUnknown of the calling ActiveX component (if the caller is an ActiveX component). If the caller is not an ActiveX component, this value may be set to NULL. Otherwise, the caller is a COM object that is contained in another component (such as an ActiveX Control within the context of an HTML page). This parameter represents the outermost IUnknown of the calling component. The function will attempt the download within the context of the ActiveX client framework and allow the caller's container to receive callbacks on the progress of the download.
szURL
  The URL to be converted to a stream object. Cannot be NULL.
dwReserved
  Reserved for future use. Must be zero.
lpfnCB
  Pointer to the caller's IBindStatusCallback interface, on which URLOpenPullStream calls OnDataAvailable when data arrives from the Internet. OnDataAvailable can return E_ABORT to abort the download. When the callback is invoked and the pstm member of the STGMEDIUM structure is not NULL, the caller can read from the stream the amount of data specified in the dwSize argument passed with the OnDataAvailable call. If the caller does not read the full amount or does not call pstmRead at all, OnDataAvailable will not be called again until this happens and Read returns E_PENDING.

The pull model is slightly more cumbersome than the push model, but it allows the client to control the amount of Internet access for the download.

The logic in the following code fragment is a typical implementation of OnDataAvailable as it is used by the URLOpenStream function:

    HRESULT CMyBindStatusCallback::OnDataAvailable( ...)
    {

        HRESULT hr = NOERROR;
        DWORD dwAmountToRead = dwSize - g_readSoFar;
        BYTE * buffer = new BYTE[ dwAmountToRead ];

        while( TRUE ) {

            DWORD dwRead;

            hr = pstgmed->pstrm->Read( buffer, dwAmountToRead, &dwRead );
            if( hr == E_PENDING ) {
                // we'll get notified again when more data comes
                return(NOERROR);
            }

            if( SUCCEEDED(hr) ) {
                .
                . // ok, process bits and keep looping
                .
            }
            else {
                // we have an error...
                return(hr);
            }
        }
    }

© 1996 Microsoft Corporation