SAMPLE: Using a URL Moniker to POST Data

Last reviewed: October 31, 1997
Article ID: Q165800
The information in this article applies to:
  • Microsoft ActiveX SDK, version 1.0

SUMMARY

Depending on the quantity of data to be transmitted in a request to an HTTP server, it may be desirable, or even necessary, to use the POST method as opposed to the GET method, which is limited to sending approximately 2K of data to the server. This article explains the requirements for using URL Monikers to POST data to an HTTP server. Sample code in the form of a dialog-based application is also provided.

The following file is available for download from the Microsoft Software Library:

 ~ Postmon.exe (size: 42091 bytes) 

For more information about downloading files from the Microsoft Software Library, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q119591
   TITLE     : How to Obtain Microsoft Support Files from Online Services

MORE INFORMATION

The PROGRESS sample included with the ActiveX SDK for Internet Explorer 3.x and the Internet Client SDK for Internet Explorer 4.0 demonstrated how to perform the GET method using a URL Moniker. Building upon that knowledge, the POSTMON sample demonstrates the modifications necessary to perform a POST.

Following is a description of the major implementation details that need to be accounted for when performing a POST using a URL Moniker:

  1. In the implementation of IBindStatusCallback::GetBindInfo, specify the action BINDVERB_POST as well as the data to be posted in a pointer to the BINDINFO structure provided by the URL Moniker. At this time, HGLOBAL is the only supported format for the data to be transmitted to the server. Take special caution when posting large quantities of data by checking the return values of memory allocation functions. If memory is limited, these functions may fail. Also observe that the sample implementation fills in the pUnkForRelease member of the STGMEDIUM data structure. This allows the client to manage the data through the reference count on the object that implements IBindStatusCallback. In this case, the client should free the data only on final release of the object that implements the callback interface:

          STDMETHODIMP
    
             CBindStatusCallback::GetBindInfo(DWORD* pgrfBINDF,
                                              BINDINFO* pbindInfo)
          {
          if (m_fRedirect && BINDVERB_POST == m_dwAction)
          {
             // You are being redirected by the server.
             // The method is changed to GET here so no data is posted below
             SetStatusText(_T("Switching method to GET"));
                m_dwAction = BINDVERB_GET;
          }
    
         *pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE
                             | BINDF_PULLDATA;
         *pgrfBINDF |= BINDF_GETNEWESTVERSION
                             | BINDF_NOWRITECACHE;
    
          // Set up the BINDINFO data structure
          pbindInfo->cbSize = sizeof(BINDINFO);
          pbindInfo->dwBindVerb = m_dwAction; // here's where the action is
          pbindInfo->szExtraInfo = NULL;
    
          // Initialize the STGMEDIUM.
          memset(&pbindInfo->stgmedData, 0, sizeof(STGMEDIUM));
          pbindInfo->grfBindInfoF = 0;
          pbindInfo->szCustomVerb = NULL;
    
          // set up action-specific members
          switch(m_dwAction)
          {
          case BINDVERB_POST:
             // only POST data when the method is POST
             if (m_hDataToPost)
             {
                // Fill the STGMEDIUM with the data to post
                // this is the only medium urlmon supports right now
                pbindInfo->stgmedData.tymed = TYMED_HGLOBAL;
                pbindInfo->stgmedData.hGlobal = m_hDataToPost;
                pbindInfo->stgmedData.pUnkForRelease =
                   (LPUNKNOWN)(LPBINDSTATUSCALLBACK)this; // maintain control
           AddRef(); // It will be freed on final release
                // the following must be exact! GlobalSize() rounds up.
           pbindInfo->cbstgmedData = m_cbDataToPost;
             }
             break;
          case BINDVERB_GET:
             break;
          default:
             return E_FAIL;
          }
    
    

  2. In the case of a redirect response from the server, a URL Moniker notifies the client of a redirect request through a call to IBindStatusCallback::OnProgress with a status code of BINDSTATUS_REDIRECTING. In this case, the client should stop posting data regardless of whether the new URL would accept it. The client should detect this status code in their implementation and set a flag in their callback implementation and modify the behavior of GetBindInfo appropriately as shown above in the m_fRedirect case. For more information on redirection, see RFC 1945 referenced below.

    RFC 2068 referenced below contains a note that states the following:

    “When automatically redirecting a POST request after receiving a 301 status code, some existing HTTP/1.0 user agents will erroneously change it into a GET request.”

    For compatibility with Netscape Navigator, the URL moniker performs such an action.

  3. The client object that implements IBindStatusCallback should also implement IHttpNegotiate. The following is a basic implementation of this method defined in this interface. Observe that when the transaction begins, in order to POST data the client must specify the additional header "Content-Type: application/x-www-form-urlencoded." This header indicates that the data being posted is URL-encoded form data. If a different Content-Type is being posted to the server, the data must be encoded according to that content-type's rules and the appropriate content type header specified via IHttpNegotiate::BeginningTransaction:

          STDMETHODIMP
          CBindStatusCallback::BeginningTransaction(LPCWSTR szURL,
    
                   LPCWSTR szHeaders,
                   DWORD dwReserved,
                   LPWSTR __RPC_FAR *pszAdditionalHeaders)
          {
             // Here's our opportunity to add headers
             if (!pszAdditionalHeaders)
             {
                return E_POINTER;
             }
    
             *pszAdditionalHeaders = NULL;
    
             // This header is required when performing a POST operation
             if (BINDVERB_POST == m_dwAction && m_hDataToPost)
             {
                const WCHAR c_wszHeaders[] =
                   L"Content-Type: application/x-www-form-urlencoded\r\n";
    
                LPWSTR wszAdditionalHeaders =
                       (LPWSTR)CoTaskMemAlloc(
                          (wcslen(c_wszHeaders)+1) *sizeof(WCHAR));
                if (!wszAdditionalHeaders)
                {
                   return E_OUTOFMEMORY;
                }
    
                wcscpy(wszAdditionalHeaders, c_wszHeaders);
                *pszAdditionalHeaders = wszAdditionalHeaders;
             }
             return NOERROR;
          }
    
          STDMETHODIMP CBindStatusCallback::OnResponse(DWORD dwResponseCode,
                      LPCWSTR szResponseHeaders,
                      LPCWSTR szRequestHeaders,
                      LPWSTR __RPC_FAR *pszAdditionalRequestHeaders)
          {
             if (!pszAdditionalRequestHeaders)
             {
                return E_POINTER;
             }
    
             *pszAdditionalRequestHeaders = NULL;
    
             return NOERROR;
          }
    
    
See the instructions below on obtaining the sample. To build the sample, refer to the README.TXT file included in the self-extracting archive. The file POSTMON.CPP contains some explanation of the sample's interface.

To use the sample, perform the following steps:

  1. Copy POSTMON.ASP and REDIR.ASP to a directory on an NT 4.0 server running Internet Information Server (IIS) version 3.0. The directory should be recognized by IIS as a virtual root with execute permission.

  2. Copy REDIR.HTM to the designated "Home Directory" by IIS, typically the \inetpub\wwwroot subdirectory. Verify that this directory has read permission.

  3. Build the POSTMON sample from the command line using NMAKE as described in the README.TXT file. NMAKE will create POSTMON.EXE on the designated output directory (in this case, POSTMON_ has been specified as the output directory).

NOTE: This NMAKE-generated POSTMON.EXE should not be confused with the downloadable, self-extracting archive of this sample of the same name.

  1. Run POSTMON_\POSTMON.EXE.

  2. Modify the POSTMON address textbox to specify a valid path to POSTMON.ASP. Note that you must not specify a local file path because a POST or a GET will only succeed if the client interacts with an HTTP server.

  3. The default method is POST as indicated by the radio button. Click submit, and observe the results.

  4. To test redirection, modify the POSTMON address textbox to specify a valid URL to REDIR.ASP, click submit and observe the results. The contents of REDIR.HTM will be displayed.

REFERENCES

Berners-Lee, T. RFC 1945, "The Hypertext Transfer Protocol -- HTTP/1.0"

Fielding, R. RFC 2068, "Hypertext Transfer Protocol -- HTTP/1.1"

Asynchronous Moniker Specification included with the ActiveX SDK

Keywords          : AXSDKUrlMon kbsample kbfile
Version           : 1.0
Platform          : WINDOWS
Issue type        : kbinfo
Solution Type     : kbfile


================================================================================


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: October 31, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.