2019. 3. 15. 13:11

URLDownloadToFile with IBindStatusCallback and IHTTPSecurity

The last parameter of URLDownloadToFile and URLDownloadToCacheFile API is the pointer of IBindStatusCallback.
This paramter is optional, so you can just set NULL; however, in my experience, you have to implement IBindStatusCallback in many cases.


 class CBindStatusCallback : public IBindStatusCallback , public IHttpSecurity
{
public:
 CBindStatusCallback()
 ~CBindStatusCallback(){}

 ///// IUnknown methods
 STDMETHOD_(ULONG,AddRef)()
        { return 1; } //non-heap-based objects:

    STDMETHOD_(ULONG,Release)()
        { return 1; } //non-heap-based objects:

    STDMETHOD(QueryInterface)(
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);


 ///// IBindStatusCallback methods.
 STDMETHOD(OnStartBinding)(
        /* [in] */ DWORD dwReserved,
        /* [in] */ IBinding __RPC_FAR *pib)
        { return E_NOTIMPL; }

  1. Implement IUnknown interface
    • AddRef, Relase Method
      • You can implement this function according to official rule, but there is trick.
      • You can make this object local variable, then you don't need to worry about it's lifetime.
      • return non-zero value: it means this object will not be destroyed.
    • QueryInterface Method
      •  HRESULT CBindStatusCallback::QueryInterface(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
        {
         if( !ppvObject)
          return E_INVALIDARG;

         if( IID_IUnknown == riid )
          *ppvObject =static_cast<IBindStatusCallback*>(this);
         else if( IID_IBindStatusCallback == riid)
          *ppvObject =static_cast<IBindStatusCallback*>(this);
         else if( IID_IWindowForBindingUI == riid)
          *ppvObject =static_cast<IWindowForBindingUI*>(this);
         else if( IID_IHttpSecurity == riid )
          *ppvObject =static_cast<IHttpSecurity*>(this);
         else
         {
          *ppvObject = NULL;
          return E_NOINTERFACE;
         }

         reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
         return S_OK;

        }


  2. Implement IBindStatusCallback interface
    • You don't need to implement every method; you can actually write 'return E_NOTIMPL' in almost cases.
    • GetBindInfo method has crucial role.
      • You can set a code page.
        • If there is non English character in URL, it can be an issue depending on the OS language setting.
      • Also, You can set several options.

       HRESULT CBindStatusCallback::GetBindInfo(
              /* [out] */ DWORD __RPC_FAR *grfBINDF,
              /* [unique][out][in] */ BINDINFO __RPC_FAR *pbindinfo)
      {

       *grfBINDF = BINDF_ASYNCHRONOUS;

       DWORD cbSize = pbindinfo->cbSize;
       memset(pbindinfo, 0, cbSize);
       pbindinfo->cbSize = cbSize;
       pbindinfo->dwCodePage = CP_UTF8;

       return S_OK;

      }


  3. Implement IHttpSecurity interface
    • You can download files even though the sever has some security problems such as a self-signed certificate.
    •   ///// IWindowForBindingUI
      HRESULT CBindStatusCallback::GetWindow(
                  /* [in] */ REFGUID rguidReason,
                  /* [out] */ HWND *phwnd)
      {
       return S_FALSE; // This indicate that no window is available.
      }


      ///// IHttpSecurity
      HRESULT CBindStatusCallback::OnSecurityProblem(
              /* [in] */ DWORD dwProblem)
      {
       return S_OK;  // Ignore securiy errors.
      }