Microsoft KB Archive/234913

= How To Provide Download/Upload Progress Information when Using WinInet =

Article ID: 234913

Article Last Modified on 7/15/2004

-

APPLIES TO


 * Microsoft Windows Internet Services (WinInet)

-



This article was previously published under Q234913



SUMMARY
Many developers who are using the WinInet functions to download or upload files on the Internet would like to provide a progress bar to indicate how much of the file transfer has completed and how much longer it will take. You can do this with the following mechanisms.



MORE INFORMATION
Using InternetSetStatusCallback to get notifications of the download progress gives you good information on how the request is progressing, including connecting status notifications. However, it does not indicate that a certain percentage of a transfer has completed.

To get the equivalent of percentage complete notifications, you need to determine the size of the transfer and then call InternetReadFile or InternetWriteFile with small buffers. Then you can calculate the percentage of the transfer as the function calls complete.

For instance, suppose you want to download a 1000 byte file. Instead of making one call to InternetReadFile with a 1000 byte buffer, you can make 10 calls to InternetReadFIle with 100 byte buffers. This way as each call to InternetReadFile completes, you know the download is another 10 percent complete.

The following code illustrates this procedure:
 * 1) include
 * 2) include
 * 3) include

void main(int argc, char *argv[]) {   if (argc != 3) {       cout << "Usage: progress  " << endl; return; }

HINTERNET hSession = InternetOpen("WinInet Progress Sample",                                     INTERNET_OPEN_TYPE_PRECONFIG,                                      NULL,                                      NULL,                                      0); HINTERNET hConnection = InternetConnect(hSession,                                            argv[1],  // Server                                            INTERNET_DEFAULT_HTTP_PORT,                                            NULL,     // Username                                            NULL,     // Password                                            INTERNET_SERVICE_HTTP,                                            0,        // Synchronous                                            NULL);    // No Context

HINTERNET hRequest = HttpOpenRequest(hConnection,                                         "GET",                                         argv[2],                                         NULL,    // Default HTTP Version                                         NULL,    // No Referer                                         (const char**)"*/*\0", // Accept                                                                // anything                                         0,       // Flags                                         NULL);   // No Context HttpSendRequest(hRequest,                   NULL,    // No extra headers                    0,       // Header length                    NULL,    // No Body                    0);      // Body length

DWORD dwContentLen; DWORD dwBufLen = sizeof(dwContentLen); if (HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwContentLen, &dwBufLen, 0))   {        // You have a content length so you can calculate percent complete char *pData = (char*)GlobalAlloc(GMEM_FIXED, dwContentLen + 1); DWORD dwReadSize = dwContentLen / 10;  // We will read 10% of data // with each read.

cout << "Download Progress:" << endl; cout << "   0--100%" << endl; cout << "    "; cout.flush;

DWORD cReadCount; DWORD dwBytesRead; char *pCopyPtr = pData; for (cReadCount = 0; cReadCount < 10; cReadCount++) {           InternetReadFile(hRequest, pCopyPtr, dwReadSize, &dwBytesRead); cout << "*"; cout.flush; pCopyPtr = pCopyPtr + dwBytesRead; }       // extra read to account for integer division round off InternetReadFile(hRequest,                        pCopyPtr,                         dwContentLen - (pCopyPtr - pData),                         &dwBytesRead); // Null terminate data pData[dwContentLen] = 0;

// Display cout << endl << "Download Complete" << endl; cout << pData; }   else {       DWORD err = GetLastError; // No content length...impossible to calculate % complete // Just read until we are done. char pData[100]; DWORD dwBytesRead = 1; while (dwBytesRead) {           InternetReadFile(hRequest, pData, 99, &dwBytesRead); pData[dwBytesRead] = 0; cout << pData; }   } } There are a few things to look out for when using this approach:

 You must know the data size before you start. The above code attempts to determine the data size by reading the Content-Length HTTP header using the HttpQueryInfo function. Although many HTTP responses include the Content-Length header, it is not required. Unless you have another mechanism for getting the data size, you will not be able to calculate progress if the Content-Length header is not included in the response. If you are attempting to upload or download from an FTP resource, you will not be able to use FtpPutFile or FtpGetFile and expect to determine progress information. You should use FtpOpenFile and then use InternetReadFile and InternetWriteFile as described above. Because providing progress information assumes you must know the data size, you can use FtpGetFileSize to get the size of an FTP resource before you download a file. Be aware that FtpGetFileSize is not always successful in getting the file size because of the variety of ways that FTP servers return directory listing information. For additional information about problems using FTP to get directory listing information, please see the following article in the Microsoft Knowledge Base:

172712 INFO: Limitations of WinInet FTP Functions



Keywords: kbhowto KB234913

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.