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.
To use WinINet functions asynchronously
- Create a context value.
- Create the skeleton of the status callback function.
- Create the code to handle the status values.
- Start the Internet session in asynchronous mode.
- Set the status callback function.
- 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:
- FtpCreateDirectory
- FtpDeleteFile
- FtpFindFirstFile
- FtpGetCurrentDirectory
- FtpGetFile
- FtpOpenFile
- FtpPutFile
- FtpRemoveDirectory
- FtpRenameFile
- FtpSetCurrentDirectory
- GopherFindFirstFile
- GopherOpenFile
- HttpEndRequest
- HttpOpenRequest
- HttpSendRequestEx
- InternetConnect
- InternetOpenUrl
- InternetReadFileEx
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