Calling WinINet Functions Asynchronously

This topic describes how to handle multiple Internet requests using the WinINet functions asynchronously.

The code from the AsyncDemo sample is used in this tutorial. AsyncDemo submits two Internet requests. The plaintext version of the resource and the returned headers displayed in edit boxes.

Implementation Steps

The AsyncDemo sample displays a dialog box with two buttons, a list box to hold the status callback data, and two sets of three edit boxes—one edit box for the URL, one edit box for the header data, and one edit box for the resource. The dialog box is defined in the Resource.rc file in the same directory as the sample code.

The following image shows the dialog box used by the AsyncDemo sample.

AsyncDemo dialog box

To use WinINet functions asynchronously

  1. Create a context value.
  2. Create the skeleton of the status callback function.
  3. Create the code to handle the status values.
  4. Start the Internet session in asynchronous mode.
  5. Set the status callback function.
  6. Start a request with the context value.

Creating a Context Value

For asynchronous Internet requests, the WinINet functions require the provision of a nonzero context value. The context value provides a way for your status callback function to track what request the status callback is coming from, and it can be used to provide access to any resources it requires to process the callback.

A context value can be any variable that can be cast to a DWORD_PTR. One possibility is to pass the address of a structure that contains the resources required by your application.

In the AsyncDemo sample, the following structure is used as the context value.

typedef struct {
    HWND          hWindow;     // Main window handle
    int           nURL;        // ID of the edit box with the URL
    int           nHeader;     // ID of the edit box for the
                               // header info
    int           nResource;   // ID of the edit box for the resource
    HINTERNET     hOpen;       // HINTERNET handle created by 
                               // InternetOpen
    HINTERNET     hResource;   // HINTERNET handle created by 
                               // InternetOpenUrl
    char          szMemo[512]; // String to store status memo
    HANDLE        hThread;     // Thread handle
    DWORD         dwThreadID;  // Thread ID
} REQUEST_CONTEXT;

Creating the Skeleton of the Status Callback Function

Other than INTERNET_STATUS_REQUEST_COMPLETE, you have a choice of which status values to capture. To display the progress of your requests, your application should capture all the status values. To stop lengthy downloads, an application should capture the INTERNET_STATUS_HANDLE_CREATED callback, which includes the HINTERNET handle for the request in the lpvStatusInformation buffer.

For more information about status callback functions, see Creating Status Callback Functions.

Creating the Code to Handle the Status Values

The AsyncDemo sample captures all the callbacks and writes a string to a list box in the dialog box. For the INTERNET_STATUS_REQUEST_COMPLETE callbacks, the AsyncDemo sample creates a separate thread to take care of getting the header data and downloading the resource.

The following example code is the callback function used in the AsyncDemo sample.

void __stdcall Juggler(HINTERNET hInternet,
                        DWORD_PTR dwContext,
                        DWORD dwInternetStatus,
                        LPVOID lpvStatusInformation,
                        DWORD dwStatusInformationLength)
{

REQUEST_CONTEXT *cpContext;
char szBuffer[256];
cpContext= (REQUEST_CONTEXT*)dwContext;

switch (dwInternetStatus)
{
    case INTERNET_STATUS_CLOSING_CONNECTION:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: CLOSING_CONNECTION (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_CONNECTED_TO_SERVER:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: CONNECTED_TO_SERVER (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_CONNECTING_TO_SERVER:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: CONNECTING_TO_SERVER (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_CONNECTION_CLOSED:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: CONNECTION_CLOSED (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_HANDLE_CLOSING:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: HANDLE_CLOSING (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_HANDLE_CREATED:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: HANDLE_CREATED (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_INTERMEDIATE_RESPONSE:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: INTERMEDIATE_RESPONSE (%d)",
                          cpContext->szMemo, 
                         dwStatusInformationLength );
        break;
    case INTERNET_STATUS_NAME_RESOLVED:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: NAME_RESOLVED (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_RECEIVING_RESPONSE:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: RECEIVING_RESPONSE (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_RESPONSE_RECEIVED:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: RESPONSE_RECEIVED (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_REDIRECT:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: REDIRECT (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_REQUEST_COMPLETE:
        // Check for errors.
        if (LPINTERNET_ASYNC_RESULT(lpvStatusInformation)->dwError == 0)
        {
            // Verify that the completed request is from AsyncDirect.
            // Be aware that strcmp assumes its parameters to be 
            // null-terminated strings.
            if (strcmp(cpContext->szMemo, "AsyncDirect"))
            {
                // Set the resource handle to the HINTERNET handle
                // returned in the callback.
                cpContext->hResource = HINTERNET(
                  LPINTERNET_ASYNC_RESULT(lpvStatusInformation)->dwResult);

                // Write the callback data to the buffer.
                StringCchPrintfA( szBuffer, 
                                  256, 
                                  "%s: REQUEST_COMPLETE (%d)",
                                  cpContext->szMemo, 
                                  dwStatusInformationLength );

                // Create a thread to handle the header and
                // resource download.
                cpContext->hThread = CreateThread(NULL, 0,
                                     (LPTHREAD_START_ROUTINE)Threader,
                                     LPVOID(cpContext),
                                     0,&cpContext->dwThreadID);
            }
            else
            {               
                StringCchPrintfA( szBuffer, 
                                  256, 
                                  "%s(%d): REQUEST_COMPLETE (%d)",
                                  cpContext->szMemo,
                                  cpContext->nURL, 
                                  dwStatusInformationLength );
            }
        }
        else
        {
            StringCchPrintfA( szBuffer, 
                   256, 
                   "%s: REQUEST_COMPLETE (%d) Error (%d) encountered",
                   cpContext->szMemo, 
                   dwStatusInformationLength,
                   GetLastError());
        }
        break;
    case INTERNET_STATUS_REQUEST_SENT:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: REQUEST_SENT (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_RESOLVING_NAME:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: RESOLVING_NAME (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    case INTERNET_STATUS_SENDING_REQUEST:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: SENDING_REQUEST (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength);
        break;
    case INTERNET_STATUS_STATE_CHANGE:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: STATE_CHANGE (%d)",
                          cpContext->szMemo, 
                          dwStatusInformationLength );
        break;
    default:
        // Write the callback data to the buffer.
        StringCchPrintfA( szBuffer, 
                          256, 
                          "%s: Unknown: Status %d Given",
                          dwInternetStatus );
        break;
    }
    
    // Add the callback data to the callback list box.
    SendDlgItemMessage(cpContext->hWindow,IDC_CallbackList,
        LB_ADDSTRING,0,(LPARAM)szBuffer);
    
}

Starting the Internet Session in Asynchronous Mode

To start an Internet session in asynchronous mode, call InternetOpen with the INTERNET_FLAG_ASYNC flag set.

The following example shows the call to InternetOpen from the WinMain function in the AsyncDemo sample.

HINTERNET hOpen    // root HINTERNET handle

hOpen = InternetOpen(lpszAgent, INTERNET_OPEN_TYPE_PRECONFIG,
                          NULL, NULL, INTERNET_FLAG_ASYNC);

Setting the Status Callback Function

To set the status callback function on the HINTERNET handle that your application requires to receive status callbacks on, your application must call the InternetSetStatusCallback function. InternetSetStatusCallback takes the HINTERNET handle and the application's status callback function and returns InternetStatusCallback.

The following example shows a call to InternetSetStatusCallback from the WinMain function in the AsyncDemo sample.

iscCallback = InternetSetStatusCallback(hOpen,
                   (INTERNET_STATUS_CALLBACK)Juggler);

Starting a Request with the Context Value

The following functions can be called asynchronously:

Note   The FtpCreateDirectory, FtpRemoveDirectory, FtpSetCurrentDirectory, FtpGetCurrentDirectory, FtpDeleteFile, and FtpRenameFile functions use the context value provided in the call to the InternetConnect function.

For each function that is called asynchronously, your status callback function requires a way to detect which function was called and to determine what it should do with the request.

For simplicity, the AsyncDemo sample uses the InternetOpenUrl function to retrieve an Internet resource. Because InternetOpenUrl is the only function that the AsyncDemo sample is calling asynchronously, the sample does not need to track calls by other functions (such as InternetConnect).

The following example shows the call to InternetOpenUrl from the AsyncDirect function in the AsyncDemo sample.

prcContext->hResource = InternetOpenUrl(hOpen, szURL,
                             NULL, 0, 0, (DWORD)&test);

See Also

AsyncDemo sample
Asynchronous Operation
Creating Status Callback Functions

Send comments about this topic to Microsoft

Build date: 2/7/2008