WinInet编程中如何使用异步
在WinInet编程中,同步的使用方法如下:
InternetOpen->InternetOpenUrl->HttpQueryInfo->InternetReadFile->InternetCloseHandle;在InternetOpenUrl和InternetReadFile时会导致程序阻塞,知道操作完成,同步的好处就是比较简单,调试方便。
异步的使用方法如下:
1)InternetOpen,需指定是异步;
2)InternetSetStatusCallback,设置回调;
3)InternetOpenUrl,需指定回调参数;
4)WaitForSingObject或WaitForMultipleObjects,接收信号量;
5)HttpQueryInfo;
6)InternetReadFileEx,需指定回调参数(CE或mobile下是InternetReadFileExA);
7)WaitForSingObject或WaitForMultipleObjects,接收信号量;
8)InternetSetStatusCallback,卸载回调;
9)InternetCloseHandle。
异步比同步要复杂了不少,重点在于回调函数。在回调中,系统会及时返回各种系统定义的HTTP消息,我们根据这些消息来设置某些信号量。在WaitForSingObject或WaitForMultipleObjects里,等待这些信号(当然也可以等待用户的取消动作)。当有正确的信号返回时,继续往下的操作。下面一个例子代码:上面的理论同样适用于wince或windows mobile平台
#include
<
windows.h
>
#include < wininet.h >
#include < iostream.h >
DWORD dwNumKSent;
DWORD dwNumKToSend;
DWORD dwNumBytesComplete = 0 ;
char lpOutBuf[ 1024 ];
HANDLE hConnectedEvent, hRequestCompleteEvent;
HINTERNET hInstance, hConnect, hRequest;
char * lpszUrl, * lpszServer;
BOOL bAllDone = FALSE;
void __stdcall Callback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpStatusInfo,
DWORD dwStatusInfoLen);
void main( int argc, char * argv[])
{
if (argc != 4 )
{
cout << " Usage: sendreqexasync <server> <url> <size in kilobytes> " << endl;
cout << " Example: sendreqexasync www.foo.com /postfolder/upload.exe 256 " << endl;
return ;
}
lpszServer = argv[ 1 ];
lpszUrl = argv[ 2 ];
dwNumKToSend = atoi(argv[ 3 ]);
FillMemory(lpOutBuf, 1024 , ' A ' );
hConnectedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hRequestCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hInstance = InternetOpen( " sendreqexasync " ,
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
INTERNET_FLAG_ASYNC);
if (hInstance == NULL)
{
cout << " InternetOpen failed, error " << GetLastError();
return ;
}
if (InternetSetStatusCallback(hInstance,
(INTERNET_STATUS_CALLBACK) & Callback) == INTERNET_INVALID_STATUS_CALLBACK)
{
cout << " InternetSetStatusCallback failed, error " << GetLastError();
return ;
}
hConnect = InternetConnect(hInstance,
lpszServer,
INTERNET_DEFAULT_HTTP_PORT,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0 ,
1 );
if (hConnect == NULL)
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << " InternetConnect failed, error " << GetLastError();
return ;
}
WaitForSingleObject(hConnectedEvent, INFINITE);
}
hRequest = HttpOpenRequest(hConnect,
" POST " ,
lpszUrl,
NULL,
NULL,
NULL,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE,
2 );
if (hRequest == NULL)
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << " HttpOpenRequest failed, error " << GetLastError();
return ;
}
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
INTERNET_BUFFERS IntBuff;
FillMemory( & IntBuff, sizeof (IntBuff), 0 );
IntBuff.dwStructSize = sizeof (IntBuff);
IntBuff.dwBufferTotal = 1024 * dwNumKToSend;
IntBuff.lpcszHeader = " Content-Type: text/text\r\n " ;
IntBuff.dwHeadersLength = lstrlen(IntBuff.lpcszHeader);
if ( ! HttpSendRequestEx(hRequest,
& IntBuff,
NULL,
0 ,
2 ))
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << " HttpSendRequestEx failed, error " << GetLastError();
return ;
}
cout << " HttpSendRequestEx called successfully " << endl;
cout.flush();
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
for (dwNumKSent = 0 ; dwNumKSent < dwNumKToSend; dwNumKSent ++ )
{
DWORD dwBytesWritten;
if ( ! InternetWriteFile(hRequest,
lpOutBuf,
1024 ,
& dwBytesWritten))
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << " InternetWriteFile failed, error " << GetLastError();
return ;
}
else
{
cout << " InternetWriteFile completing asynchronously " << endl;
cout.flush();
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
}
}
cout << " Calling HttpEndRequest " << endl;
cout.flush();
if ( ! HttpEndRequest(hRequest, NULL, HSR_INITIATE, 2 ))
{
if (GetLastError() == ERROR_IO_PENDING)
{
cout << " HttpEndRequest called " << endl;
cout.flush();
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
else
{
cout << " HttpEndRequest failed, error " << GetLastError() << endl;
return ;
}
}
cout << " ------------------- Read the response ------------------- " << endl;
char lpReadBuff[ 256 ];
do
{
INTERNET_BUFFERS InetBuff;
FillMemory( & InetBuff, sizeof (InetBuff), 0 );
InetBuff.dwStructSize = sizeof (InetBuff);
InetBuff.lpvBuffer = lpReadBuff;
InetBuff.dwBufferLength = sizeof (lpReadBuff) - 1 ;
cout << " Calling InternetReadFileEx " << endl;
cout.flush();
if ( ! InternetReadFileEx(hRequest,
& InetBuff,
0 , 2 ))
{
if (GetLastError() == ERROR_IO_PENDING)
{
cout << " Waiting for InternetReadFile to complete " << endl;
cout.flush();
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
else
{
cout << " InternetReadFileEx failed, error " << GetLastError();
cout.flush();
return ;
}
}
lpReadBuff[InetBuff.dwBufferLength] = 0 ;
cout << lpReadBuff;
cout.flush();
if (InetBuff.dwBufferLength == 0 )
bAllDone = TRUE;
} while (bAllDone == FALSE);
cout << endl << endl << " ------------------- Request Complete ---------------- " << endl;
}
void __stdcall Callback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpStatusInfo,
DWORD dwStatusInfoLen)
{
cout << " Callback dwInternetStatus: " << dwInternetStatus << " Context: " << dwContext << endl;
cout.flush();
switch (dwContext)
{
case 1 : // Connection handle
if (dwInternetStatus == INTERNET_STATUS_HANDLE_CREATED)
{
INTERNET_ASYNC_RESULT * pRes = (INTERNET_ASYNC_RESULT * )lpStatusInfo;
hConnect = (HINTERNET)pRes -> dwResult;
cout << " Connect handle created " << endl;
cout.flush();
SetEvent(hConnectedEvent);
}
break ;
case 2 : // Request handle
switch (dwInternetStatus)
{
case INTERNET_STATUS_HANDLE_CREATED:
{
INTERNET_ASYNC_RESULT * pRes = (INTERNET_ASYNC_RESULT * )lpStatusInfo;
hRequest = (HINTERNET)pRes -> dwResult;
cout << " Request handle created " << endl;
cout.flush();
}
break ;
case INTERNET_STATUS_REQUEST_SENT:
{
DWORD * lpBytesSent = (DWORD * )lpStatusInfo;
cout << " Bytes Sent: " << * lpBytesSent << endl;
dwNumBytesComplete += * lpBytesSent;
}
break ;
case INTERNET_STATUS_REQUEST_COMPLETE:
{
INTERNET_ASYNC_RESULT * pAsyncRes = (INTERNET_ASYNC_RESULT * )lpStatusInfo;
cout << " Function call finished " << endl;
cout << " dwResult: " << pAsyncRes -> dwResult << endl;
cout << " dwError: " << pAsyncRes -> dwError << endl;
cout.flush();
SetEvent(hRequestCompleteEvent);
}
break ;
case INTERNET_STATUS_RECEIVING_RESPONSE:
cout << " Receiving Response " << endl;
cout.flush();
break ;
case INTERNET_STATUS_RESPONSE_RECEIVED:
{
DWORD * dwBytesReceived = (DWORD * )lpStatusInfo;
cout << " Received " << * dwBytesReceived << endl;
cout.flush();
}
}
}
}
参考的异步类:
#include < wininet.h >
#include < iostream.h >
DWORD dwNumKSent;
DWORD dwNumKToSend;
DWORD dwNumBytesComplete = 0 ;
char lpOutBuf[ 1024 ];
HANDLE hConnectedEvent, hRequestCompleteEvent;
HINTERNET hInstance, hConnect, hRequest;
char * lpszUrl, * lpszServer;
BOOL bAllDone = FALSE;
void __stdcall Callback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpStatusInfo,
DWORD dwStatusInfoLen);
void main( int argc, char * argv[])
{
if (argc != 4 )
{
cout << " Usage: sendreqexasync <server> <url> <size in kilobytes> " << endl;
cout << " Example: sendreqexasync www.foo.com /postfolder/upload.exe 256 " << endl;
return ;
}
lpszServer = argv[ 1 ];
lpszUrl = argv[ 2 ];
dwNumKToSend = atoi(argv[ 3 ]);
FillMemory(lpOutBuf, 1024 , ' A ' );
hConnectedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hRequestCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hInstance = InternetOpen( " sendreqexasync " ,
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
INTERNET_FLAG_ASYNC);
if (hInstance == NULL)
{
cout << " InternetOpen failed, error " << GetLastError();
return ;
}
if (InternetSetStatusCallback(hInstance,
(INTERNET_STATUS_CALLBACK) & Callback) == INTERNET_INVALID_STATUS_CALLBACK)
{
cout << " InternetSetStatusCallback failed, error " << GetLastError();
return ;
}
hConnect = InternetConnect(hInstance,
lpszServer,
INTERNET_DEFAULT_HTTP_PORT,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0 ,
1 );
if (hConnect == NULL)
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << " InternetConnect failed, error " << GetLastError();
return ;
}
WaitForSingleObject(hConnectedEvent, INFINITE);
}
hRequest = HttpOpenRequest(hConnect,
" POST " ,
lpszUrl,
NULL,
NULL,
NULL,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE,
2 );
if (hRequest == NULL)
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << " HttpOpenRequest failed, error " << GetLastError();
return ;
}
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
INTERNET_BUFFERS IntBuff;
FillMemory( & IntBuff, sizeof (IntBuff), 0 );
IntBuff.dwStructSize = sizeof (IntBuff);
IntBuff.dwBufferTotal = 1024 * dwNumKToSend;
IntBuff.lpcszHeader = " Content-Type: text/text\r\n " ;
IntBuff.dwHeadersLength = lstrlen(IntBuff.lpcszHeader);
if ( ! HttpSendRequestEx(hRequest,
& IntBuff,
NULL,
0 ,
2 ))
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << " HttpSendRequestEx failed, error " << GetLastError();
return ;
}
cout << " HttpSendRequestEx called successfully " << endl;
cout.flush();
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
for (dwNumKSent = 0 ; dwNumKSent < dwNumKToSend; dwNumKSent ++ )
{
DWORD dwBytesWritten;
if ( ! InternetWriteFile(hRequest,
lpOutBuf,
1024 ,
& dwBytesWritten))
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << " InternetWriteFile failed, error " << GetLastError();
return ;
}
else
{
cout << " InternetWriteFile completing asynchronously " << endl;
cout.flush();
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
}
}
cout << " Calling HttpEndRequest " << endl;
cout.flush();
if ( ! HttpEndRequest(hRequest, NULL, HSR_INITIATE, 2 ))
{
if (GetLastError() == ERROR_IO_PENDING)
{
cout << " HttpEndRequest called " << endl;
cout.flush();
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
else
{
cout << " HttpEndRequest failed, error " << GetLastError() << endl;
return ;
}
}
cout << " ------------------- Read the response ------------------- " << endl;
char lpReadBuff[ 256 ];
do
{
INTERNET_BUFFERS InetBuff;
FillMemory( & InetBuff, sizeof (InetBuff), 0 );
InetBuff.dwStructSize = sizeof (InetBuff);
InetBuff.lpvBuffer = lpReadBuff;
InetBuff.dwBufferLength = sizeof (lpReadBuff) - 1 ;
cout << " Calling InternetReadFileEx " << endl;
cout.flush();
if ( ! InternetReadFileEx(hRequest,
& InetBuff,
0 , 2 ))
{
if (GetLastError() == ERROR_IO_PENDING)
{
cout << " Waiting for InternetReadFile to complete " << endl;
cout.flush();
WaitForSingleObject(hRequestCompleteEvent, INFINITE);
}
else
{
cout << " InternetReadFileEx failed, error " << GetLastError();
cout.flush();
return ;
}
}
lpReadBuff[InetBuff.dwBufferLength] = 0 ;
cout << lpReadBuff;
cout.flush();
if (InetBuff.dwBufferLength == 0 )
bAllDone = TRUE;
} while (bAllDone == FALSE);
cout << endl << endl << " ------------------- Request Complete ---------------- " << endl;
}
void __stdcall Callback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpStatusInfo,
DWORD dwStatusInfoLen)
{
cout << " Callback dwInternetStatus: " << dwInternetStatus << " Context: " << dwContext << endl;
cout.flush();
switch (dwContext)
{
case 1 : // Connection handle
if (dwInternetStatus == INTERNET_STATUS_HANDLE_CREATED)
{
INTERNET_ASYNC_RESULT * pRes = (INTERNET_ASYNC_RESULT * )lpStatusInfo;
hConnect = (HINTERNET)pRes -> dwResult;
cout << " Connect handle created " << endl;
cout.flush();
SetEvent(hConnectedEvent);
}
break ;
case 2 : // Request handle
switch (dwInternetStatus)
{
case INTERNET_STATUS_HANDLE_CREATED:
{
INTERNET_ASYNC_RESULT * pRes = (INTERNET_ASYNC_RESULT * )lpStatusInfo;
hRequest = (HINTERNET)pRes -> dwResult;
cout << " Request handle created " << endl;
cout.flush();
}
break ;
case INTERNET_STATUS_REQUEST_SENT:
{
DWORD * lpBytesSent = (DWORD * )lpStatusInfo;
cout << " Bytes Sent: " << * lpBytesSent << endl;
dwNumBytesComplete += * lpBytesSent;
}
break ;
case INTERNET_STATUS_REQUEST_COMPLETE:
{
INTERNET_ASYNC_RESULT * pAsyncRes = (INTERNET_ASYNC_RESULT * )lpStatusInfo;
cout << " Function call finished " << endl;
cout << " dwResult: " << pAsyncRes -> dwResult << endl;
cout << " dwError: " << pAsyncRes -> dwError << endl;
cout.flush();
SetEvent(hRequestCompleteEvent);
}
break ;
case INTERNET_STATUS_RECEIVING_RESPONSE:
cout << " Receiving Response " << endl;
cout.flush();
break ;
case INTERNET_STATUS_RESPONSE_RECEIVED:
{
DWORD * dwBytesReceived = (DWORD * )lpStatusInfo;
cout << " Received " << * dwBytesReceived << endl;
cout.flush();
}
}
}
}
1
include
<
wininet.h
>
2 #include < mmsystem.h >
3
4 class AsyncWinINet
5 {
6 public :
7 typedef void ( * notify_fp)( const StringMap & );
8
9 class thread_info
10 {
11 public :
12 thread_info( const String & _url, // 请求下载的地址(in)
13 const StringMap & _request_headrs, // 请求头request_headrs(in)
14 const notify_fp & _pfp, // 下载进度通知回调函数指针
15 const StringMap & _pfp_param,
16 String & _response_headrs, // 返回头response_headrs(out)
17 const String & _saved_filename, // 下载内容保存文件名(in)
18 String & _response_content, // 返回内容(out)
19 size_t _read_content_size) // 控制保存在response_content中内容的长度(in)) :
20 : request_headrs(_request_headrs), pfp(_pfp),
21 pfp_param(_pfp_param), // pfp函数传回参数
22 response_headrs(_response_headrs), saved_filename(_saved_filename),
23 response_content(_response_content), read_content_size(_read_content_size)
24 {
25 this -> response_headrs.clear();
26 this -> response_content.clear();
27 this -> url = StringUtil::EncodeURIComponent(_url);
28 for ( int i = 0 ; i < 3 ; ++ i)
29 {
30 this -> hEvent[i] = CreateEvent(NULL,TRUE,FALSE,NULL);
31 }
32 }
33
34 HANDLE hThread;
35 DWORD dwThreadID;
36 HANDLE hCallbackThread;
37 DWORD dwCallbackThreadID;
38 HANDLE hEvent[ 3 ];
39 LPVOID hInternet;
40 LPVOID hFile;
41 DWORD dwStatusCode;
42 DWORD dwContentLength;
43
44 String url; // 请求下载的地址(in)
45 const StringMap & request_headrs; // 请求头request_headrs(in)
46 const notify_fp & pfp; // 下载进度通知回调函数指针
47 const StringMap & pfp_param; // pfp函数传回参数
48
49 String & response_headrs; // 返回头response_headrs(out)
50 const String & saved_filename; // 下载内容保存文件名(in)
51 String & response_content; // 返回内容(out)
52 size_t read_content_size; // 控制保存在response_content中内容的长度(in)
53 };
54
55 /* ******************************************************************************
56 * 函数:download
57 * 功能:下载,返回WinINet_ERR_CODE值
58 * 说明:关于notify_fp 类型说明: 函数的参数为StringMap类型,传回的变量名与变量值
59 * 2007-12
60 ****************************************************************************** */
61 static DWORD download( const String & url, // 请求下载的地址(in)
62 const StringMap & request_headrs, // 请求头request_headrs(in)
63 const notify_fp & pfp, // 下载进度通知回调函数指针
64 const StringMap & pfp_param, // pfp函数传回参数
65 String & response_headrs, // 返回头response_headrs(out)
66 const String & saved_filename, // 下载内容保存文件名(in)
67 String & response_content, // 返回内容(out)
68 size_t read_content_size = 0 ); // 控制保存在response_content中内容的长度(in)
69
70 protected :
71 static BOOL WaitExitEvent(thread_info * p);
72 static DWORD WINAPI AsyncThread(LPVOID lpParameter);
73 static DWORD WINAPI AsyncCallbackThread(LPVOID lpParameter);
74 static VOID CALLBACK AsyncInternetCallback(HINTERNET hInternet,
75 DWORD dwContext,
76 DWORD dwInternetStatus,
77 LPVOID lpvStatusInformation,
78 DWORD dwStatusInformationLength);
79
80 };
81
2 #include < mmsystem.h >
3
4 class AsyncWinINet
5 {
6 public :
7 typedef void ( * notify_fp)( const StringMap & );
8
9 class thread_info
10 {
11 public :
12 thread_info( const String & _url, // 请求下载的地址(in)
13 const StringMap & _request_headrs, // 请求头request_headrs(in)
14 const notify_fp & _pfp, // 下载进度通知回调函数指针
15 const StringMap & _pfp_param,
16 String & _response_headrs, // 返回头response_headrs(out)
17 const String & _saved_filename, // 下载内容保存文件名(in)
18 String & _response_content, // 返回内容(out)
19 size_t _read_content_size) // 控制保存在response_content中内容的长度(in)) :
20 : request_headrs(_request_headrs), pfp(_pfp),
21 pfp_param(_pfp_param), // pfp函数传回参数
22 response_headrs(_response_headrs), saved_filename(_saved_filename),
23 response_content(_response_content), read_content_size(_read_content_size)
24 {
25 this -> response_headrs.clear();
26 this -> response_content.clear();
27 this -> url = StringUtil::EncodeURIComponent(_url);
28 for ( int i = 0 ; i < 3 ; ++ i)
29 {
30 this -> hEvent[i] = CreateEvent(NULL,TRUE,FALSE,NULL);
31 }
32 }
33
34 HANDLE hThread;
35 DWORD dwThreadID;
36 HANDLE hCallbackThread;
37 DWORD dwCallbackThreadID;
38 HANDLE hEvent[ 3 ];
39 LPVOID hInternet;
40 LPVOID hFile;
41 DWORD dwStatusCode;
42 DWORD dwContentLength;
43
44 String url; // 请求下载的地址(in)
45 const StringMap & request_headrs; // 请求头request_headrs(in)
46 const notify_fp & pfp; // 下载进度通知回调函数指针
47 const StringMap & pfp_param; // pfp函数传回参数
48
49 String & response_headrs; // 返回头response_headrs(out)
50 const String & saved_filename; // 下载内容保存文件名(in)
51 String & response_content; // 返回内容(out)
52 size_t read_content_size; // 控制保存在response_content中内容的长度(in)
53 };
54
55 /* ******************************************************************************
56 * 函数:download
57 * 功能:下载,返回WinINet_ERR_CODE值
58 * 说明:关于notify_fp 类型说明: 函数的参数为StringMap类型,传回的变量名与变量值
59 * 2007-12
60 ****************************************************************************** */
61 static DWORD download( const String & url, // 请求下载的地址(in)
62 const StringMap & request_headrs, // 请求头request_headrs(in)
63 const notify_fp & pfp, // 下载进度通知回调函数指针
64 const StringMap & pfp_param, // pfp函数传回参数
65 String & response_headrs, // 返回头response_headrs(out)
66 const String & saved_filename, // 下载内容保存文件名(in)
67 String & response_content, // 返回内容(out)
68 size_t read_content_size = 0 ); // 控制保存在response_content中内容的长度(in)
69
70 protected :
71 static BOOL WaitExitEvent(thread_info * p);
72 static DWORD WINAPI AsyncThread(LPVOID lpParameter);
73 static DWORD WINAPI AsyncCallbackThread(LPVOID lpParameter);
74 static VOID CALLBACK AsyncInternetCallback(HINTERNET hInternet,
75 DWORD dwContext,
76 DWORD dwInternetStatus,
77 LPVOID lpvStatusInformation,
78 DWORD dwStatusInformationLength);
79
80 };
81
1
#include
"
AsyncWinINet.h
"
2
3 #include " stdafx.h "
4
5 #pragma comment(lib, " Winmm.lib " )
6 #pragma comment(lib, " Wininet.lib " )
7
8 DWORD AsyncWinINet::download( const Fagex::String & url, const Fagex::StringMap & request_headrs,
9 const Fagex::AsyncWinINet::notify_fp & pfp, const Fagex::StringMap & pfp_param, Fagex::String & response_headrs,
10 const Fagex::String & saved_filename, Fagex::String & response_content, size_t read_content_size)
11 {
12 thread_info info(url, request_headrs, pfp,
13 pfp_param, response_headrs, saved_filename,
14 response_content, read_content_size);
15
16 info.hThread = CreateThread(NULL,
17 0 ,
18 AsyncWinINet::AsyncThread,
19 & info,
20 NULL,
21 & info.dwThreadID);
22
23 WaitForSingleObject(info.hThread, INFINITE); // 等待子线程安全退出
24 CloseHandle(info.hThread); // 关闭线程句柄
25
26 return TRUE;
27 }
28
29 // ---------------------------------------------------------------------
30 DWORD WINAPI AsyncWinINet::AsyncThread(LPVOID lpParameter)
31 {
32 thread_info * p = (thread_info * )lpParameter;
33
34 // a. 使用标记 INTERNET_FLAG_ASYNC 初始化 InternetOpen
35 String user_agent( " Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler ; .NET CLR 2.0.50727) " );
36 StringMap iheadrs(p -> request_headrs.begin(), p -> request_headrs.end());
37 StringMap::iterator it = iheadrs.find( " User-Agent " );
38 if (it == iheadrs.end()) iheadrs[ " User-Agent " ] = user_agent;
39 else user_agent = it -> second;
40
41 p -> hInternet = InternetOpen(user_agent.c_str(),
42 INTERNET_OPEN_TYPE_PRECONFIG,
43 NULL,
44 NULL,
45 INTERNET_FLAG_ASYNC);
46
47 // ResetEvent(p->hEvent[0]);
48 // p->hCallbackThread = CreateThread(NULL,
49 // 0,
50 // AsyncWinINet::AsyncCallbackThread,
51 // p,
52 // NULL,
53 // &p->dwCallbackThreadID);
54 // WaitForSingleObject(p->hEvent[0], INFINITE); // 等待回调函数设置成功事件
55 InternetSetStatusCallback(p -> hInternet, AsyncWinINet::AsyncInternetCallback);
56
57 String sheadrs;
58 for (it = iheadrs.begin(); it != iheadrs.end(); ++ it)
59 {
60 sheadrs += it -> first + " : " + it -> second;
61 if (it -> second.find(StringUtil::enter) == String::npos) { sheadrs += StringUtil::enter; }
62 }
63 sheadrs += StringUtil::enter;
64
65 DWORD start_time = timeGetTime();
66 ResetEvent(p -> hEvent[ 0 ]); // 重置句柄被创建事件
67 p -> hFile = InternetOpenUrl(p -> hInternet, p -> url.c_str(), sheadrs.c_str(), sheadrs.length(),
68 INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, (DWORD)p);
69
70 FILE * fp = fopen(p -> saved_filename.c_str(), " w+ " );
71 while ( true )
72 {
73 if (NULL == p -> hFile)
74 {
75 DWORD dwError = ::GetLastError();
76 if (ERROR_IO_PENDING == dwError || ERROR_SUCCESS == dwError)
77 {
78 if (WaitExitEvent(p)) { break ; }
79 }
80 else break ;
81 }
82
83 // 读取返回文件头
84 DWORD dwLength = 0 ;
85 LPVOID lpOutBuffer = NULL;
86 while ( true ) // 读取response_headrs数据
87 {
88 if ( ! HttpQueryInfo(p -> hFile, HTTP_QUERY_RAW_HEADERS_CRLF,
89 lpOutBuffer, & dwLength, NULL))
90 {
91 DWORD err_code = GetLastError();
92 if (err_code == ERROR_HTTP_HEADER_NOT_FOUND) break ;
93 else if (err_code == ERROR_INSUFFICIENT_BUFFER)
94 {
95 lpOutBuffer = new char [dwLength];
96 continue ;
97 }
98 else break ;
99 }
100 break ;
101 }
102 if (lpOutBuffer != NULL)
103 {
104 p -> response_headrs.append(( char * )lpOutBuffer,dwLength);
105 delete [] lpOutBuffer;
106 }
107
108 // e. 使用 HttpQueryInfo 分析头信息 HttpQueryInfo 使用非阻塞方式,所以不用等待
109 DWORD dwStatusSize = sizeof (p -> dwStatusCode);
110 if (FALSE == HttpQueryInfo(p -> hFile, // 获取返回状态码
111 HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
112 & p -> dwStatusCode, & dwStatusSize, NULL)) { break ; }
113
114 // 判断状态码是不是 200
115 if (HTTP_STATUS_OK != p -> dwStatusCode) break ;
116
117 StringMap msgMap(p -> pfp_param.begin(), p -> pfp_param.end());
118 msgMap[ " url " ] = p -> url;
119
120 // 获取返回的Content-Length
121 // DWORD dwLengthSize = sizeof(p->dwContentLength);
122 // if (FALSE == HttpQueryInfo(p->hFile,
123 // HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
124 // &p->dwContentLength, &dwLengthSize, NULL)) { p->dwContentLength = 0; }
125
126 // f. 使用标记 IRF_ASYNC 读数据 InternetReadFileEx
127 // 为了向主线程报告进度,我们设置每次读数据最多 1024 字节
128
129 char lpvBuffer[ 1024 ];
130 p -> dwContentLength = 0 ; // Content-Length: 202749
131 while ( true )
132 {
133 INTERNET_BUFFERS i_buf = { 0 };
134 i_buf.dwStructSize = sizeof (INTERNET_BUFFERS);
135 i_buf.lpvBuffer = lpvBuffer;
136 i_buf.dwBufferLength = 1024 ;
137
138 // 重置读数据事件
139 ResetEvent(p -> hEvent[ 0 ]);
140 if (FALSE == InternetReadFileEx(p -> hFile, & i_buf, IRF_ASYNC, (DWORD)p))
141 {
142 if (ERROR_IO_PENDING == ::GetLastError())
143 {
144 if (WaitExitEvent(p)) break ;
145 }
146 else break ;
147 }
148 else
149 {
150 // 在网络传输速度快,步长较小的情况下,InternetReadFileEx 经常会直接返回成功,
151 // 因此要判断是否发生了用户要求终止子线程事件。
152 if (WAIT_OBJECT_0 == WaitForSingleObject(p -> hEvent[ 2 ], 0 ))
153 {
154 ResetEvent(p -> hEvent[ 2 ]);
155 break ;
156 }
157 }
158
159 if (i_buf.dwBufferLength == 0 )
160 {
161 DWORD time = timeGetTime() - start_time;
162 if (time != 0 )
163 {
164 Real speed = (Real)p -> dwContentLength;
165 speed /= ((Real)time) / 1000.0f ;
166 speed /= 1024.0f ;
167 msgMap[ " speed " ] = StringUtil::toString((DWORD)speed);
168 }
169 if (p -> pfp) p -> pfp(msgMap);
170 break ;
171 }
172 if (fp)
173 {
174 fwrite(i_buf.lpvBuffer, sizeof ( char ), i_buf.dwBufferLength, fp);
175 }
176 if (p -> read_content_size > p -> response_content.size())
177 {
178 p -> response_content.append(( char * )i_buf.lpvBuffer, i_buf.dwBufferLength);
179 }
180 p -> dwContentLength += i_buf.dwBufferLength;
181 }
182 break ;
183 }
184
185 if (fp)
186 {
187 fflush(fp); fclose(fp); fp = NULL;
188 }
189
190 if (p -> hFile)
191 {
192 InternetCloseHandle(p -> hFile); // 关闭 m_hFile
193 while ( ! WaitExitEvent(p)) // 等待句柄被关闭事件或者要求子线程退出事件
194 {
195 ResetEvent(p -> hEvent[ 0 ]);
196 }
197 }
198
199 // 设置子线程退出事件,通知回调线程退出
200 SetEvent(p -> hEvent[ 2 ]);
201
202 // 等待回调线程安全退出
203 // WaitForSingleObject(p->hCallbackThread, INFINITE);
204 // CloseHandle(p->hCallbackThread);
205
206 // 注销回调函数
207 InternetSetStatusCallback(p -> hInternet, NULL);
208 InternetCloseHandle(p -> hInternet);
209
210 return TRUE;
211 }
212
213 // ------------------------------------------------------------------------------------
214 DWORD WINAPI AsyncWinINet::AsyncCallbackThread(LPVOID lpParameter)
215 {
216 thread_info * p = (thread_info * )lpParameter;
217 InternetSetStatusCallback(p -> hInternet, AsyncWinINet::AsyncInternetCallback);
218
219 // 通知子线程回调函数设置成功,子线程可以继续工作
220 SetEvent(p -> hEvent[ 0 ]);
221
222 // 等待用户终止事件或者子线程结束事件
223 // 子线程结束前需要设置子线程结束事件,并等待回调线程结束
224 WaitForSingleObject(p -> hEvent[ 2 ], INFINITE);
225
226 return 0 ;
227 }
228
229 // ----------------------------------------------------------------------------
230 VOID CALLBACK AsyncWinINet::AsyncInternetCallback(HINTERNET hInternet,
231 DWORD dwContext,
232 DWORD dwInternetStatus,
233 LPVOID lpvStatusInformation,
234 DWORD dwStatusInformationLength)
235 {
236 thread_info * p = (thread_info * )dwContext;
237
238 // 在我们的应用中,我们只关心下面三个状态
239 switch (dwInternetStatus)
240 {
241 // 句柄被创建
242 case INTERNET_STATUS_HANDLE_CREATED:
243 p -> hFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)
244 (lpvStatusInformation)) -> dwResult);
245 break ;
246
247 // 句柄被关闭
248 case INTERNET_STATUS_HANDLE_CLOSING:
249 SetEvent(p -> hEvent[ 1 ]);
250 break ;
251
252 // 一个请求完成,比如一次句柄创建的请求,或者一次读数据的请求
253 case INTERNET_STATUS_REQUEST_COMPLETE:
254 if (ERROR_SUCCESS == ((LPINTERNET_ASYNC_RESULT)
255 (lpvStatusInformation)) -> dwError)
256 {
257 // 设置句柄被创建事件或者读数据成功完成事件
258 SetEvent(p -> hEvent[ 0 ]);
259 }
260 else
261 {
262 // 如果发生错误,则设置子线程退出事件 这里也是一个陷阱,经常会忽视处理这个错误,
263 SetEvent(p -> hEvent[ 2 ]);
264 }
265 break ;
266
267 case INTERNET_STATUS_CONNECTION_CLOSED:
268 SetEvent(p -> hEvent[ 2 ]);
269 break ;
270
271 }
272 }
273
274 // --------------------------------------------------------------------
275 BOOL AsyncWinINet::WaitExitEvent(thread_info * p)
276 {
277 DWORD dwRet = WaitForMultipleObjects( 3 , p -> hEvent, FALSE, INFINITE);
278
279 switch (dwRet)
280 {
281 case WAIT_OBJECT_0: // 句柄被创建事件或者读数据请求成功完成事件
282 case WAIT_OBJECT_0 + 1 : // 句柄被关闭事件
283 case WAIT_OBJECT_0 + 2 : // 用户要求终止子线程事件或者发生错误事件
284 break ;
285 }
286 return WAIT_OBJECT_0 != dwRet;
287 }
288
2
3 #include " stdafx.h "
4
5 #pragma comment(lib, " Winmm.lib " )
6 #pragma comment(lib, " Wininet.lib " )
7
8 DWORD AsyncWinINet::download( const Fagex::String & url, const Fagex::StringMap & request_headrs,
9 const Fagex::AsyncWinINet::notify_fp & pfp, const Fagex::StringMap & pfp_param, Fagex::String & response_headrs,
10 const Fagex::String & saved_filename, Fagex::String & response_content, size_t read_content_size)
11 {
12 thread_info info(url, request_headrs, pfp,
13 pfp_param, response_headrs, saved_filename,
14 response_content, read_content_size);
15
16 info.hThread = CreateThread(NULL,
17 0 ,
18 AsyncWinINet::AsyncThread,
19 & info,
20 NULL,
21 & info.dwThreadID);
22
23 WaitForSingleObject(info.hThread, INFINITE); // 等待子线程安全退出
24 CloseHandle(info.hThread); // 关闭线程句柄
25
26 return TRUE;
27 }
28
29 // ---------------------------------------------------------------------
30 DWORD WINAPI AsyncWinINet::AsyncThread(LPVOID lpParameter)
31 {
32 thread_info * p = (thread_info * )lpParameter;
33
34 // a. 使用标记 INTERNET_FLAG_ASYNC 初始化 InternetOpen
35 String user_agent( " Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler ; .NET CLR 2.0.50727) " );
36 StringMap iheadrs(p -> request_headrs.begin(), p -> request_headrs.end());
37 StringMap::iterator it = iheadrs.find( " User-Agent " );
38 if (it == iheadrs.end()) iheadrs[ " User-Agent " ] = user_agent;
39 else user_agent = it -> second;
40
41 p -> hInternet = InternetOpen(user_agent.c_str(),
42 INTERNET_OPEN_TYPE_PRECONFIG,
43 NULL,
44 NULL,
45 INTERNET_FLAG_ASYNC);
46
47 // ResetEvent(p->hEvent[0]);
48 // p->hCallbackThread = CreateThread(NULL,
49 // 0,
50 // AsyncWinINet::AsyncCallbackThread,
51 // p,
52 // NULL,
53 // &p->dwCallbackThreadID);
54 // WaitForSingleObject(p->hEvent[0], INFINITE); // 等待回调函数设置成功事件
55 InternetSetStatusCallback(p -> hInternet, AsyncWinINet::AsyncInternetCallback);
56
57 String sheadrs;
58 for (it = iheadrs.begin(); it != iheadrs.end(); ++ it)
59 {
60 sheadrs += it -> first + " : " + it -> second;
61 if (it -> second.find(StringUtil::enter) == String::npos) { sheadrs += StringUtil::enter; }
62 }
63 sheadrs += StringUtil::enter;
64
65 DWORD start_time = timeGetTime();
66 ResetEvent(p -> hEvent[ 0 ]); // 重置句柄被创建事件
67 p -> hFile = InternetOpenUrl(p -> hInternet, p -> url.c_str(), sheadrs.c_str(), sheadrs.length(),
68 INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, (DWORD)p);
69
70 FILE * fp = fopen(p -> saved_filename.c_str(), " w+ " );
71 while ( true )
72 {
73 if (NULL == p -> hFile)
74 {
75 DWORD dwError = ::GetLastError();
76 if (ERROR_IO_PENDING == dwError || ERROR_SUCCESS == dwError)
77 {
78 if (WaitExitEvent(p)) { break ; }
79 }
80 else break ;
81 }
82
83 // 读取返回文件头
84 DWORD dwLength = 0 ;
85 LPVOID lpOutBuffer = NULL;
86 while ( true ) // 读取response_headrs数据
87 {
88 if ( ! HttpQueryInfo(p -> hFile, HTTP_QUERY_RAW_HEADERS_CRLF,
89 lpOutBuffer, & dwLength, NULL))
90 {
91 DWORD err_code = GetLastError();
92 if (err_code == ERROR_HTTP_HEADER_NOT_FOUND) break ;
93 else if (err_code == ERROR_INSUFFICIENT_BUFFER)
94 {
95 lpOutBuffer = new char [dwLength];
96 continue ;
97 }
98 else break ;
99 }
100 break ;
101 }
102 if (lpOutBuffer != NULL)
103 {
104 p -> response_headrs.append(( char * )lpOutBuffer,dwLength);
105 delete [] lpOutBuffer;
106 }
107
108 // e. 使用 HttpQueryInfo 分析头信息 HttpQueryInfo 使用非阻塞方式,所以不用等待
109 DWORD dwStatusSize = sizeof (p -> dwStatusCode);
110 if (FALSE == HttpQueryInfo(p -> hFile, // 获取返回状态码
111 HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
112 & p -> dwStatusCode, & dwStatusSize, NULL)) { break ; }
113
114 // 判断状态码是不是 200
115 if (HTTP_STATUS_OK != p -> dwStatusCode) break ;
116
117 StringMap msgMap(p -> pfp_param.begin(), p -> pfp_param.end());
118 msgMap[ " url " ] = p -> url;
119
120 // 获取返回的Content-Length
121 // DWORD dwLengthSize = sizeof(p->dwContentLength);
122 // if (FALSE == HttpQueryInfo(p->hFile,
123 // HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
124 // &p->dwContentLength, &dwLengthSize, NULL)) { p->dwContentLength = 0; }
125
126 // f. 使用标记 IRF_ASYNC 读数据 InternetReadFileEx
127 // 为了向主线程报告进度,我们设置每次读数据最多 1024 字节
128
129 char lpvBuffer[ 1024 ];
130 p -> dwContentLength = 0 ; // Content-Length: 202749
131 while ( true )
132 {
133 INTERNET_BUFFERS i_buf = { 0 };
134 i_buf.dwStructSize = sizeof (INTERNET_BUFFERS);
135 i_buf.lpvBuffer = lpvBuffer;
136 i_buf.dwBufferLength = 1024 ;
137
138 // 重置读数据事件
139 ResetEvent(p -> hEvent[ 0 ]);
140 if (FALSE == InternetReadFileEx(p -> hFile, & i_buf, IRF_ASYNC, (DWORD)p))
141 {
142 if (ERROR_IO_PENDING == ::GetLastError())
143 {
144 if (WaitExitEvent(p)) break ;
145 }
146 else break ;
147 }
148 else
149 {
150 // 在网络传输速度快,步长较小的情况下,InternetReadFileEx 经常会直接返回成功,
151 // 因此要判断是否发生了用户要求终止子线程事件。
152 if (WAIT_OBJECT_0 == WaitForSingleObject(p -> hEvent[ 2 ], 0 ))
153 {
154 ResetEvent(p -> hEvent[ 2 ]);
155 break ;
156 }
157 }
158
159 if (i_buf.dwBufferLength == 0 )
160 {
161 DWORD time = timeGetTime() - start_time;
162 if (time != 0 )
163 {
164 Real speed = (Real)p -> dwContentLength;
165 speed /= ((Real)time) / 1000.0f ;
166 speed /= 1024.0f ;
167 msgMap[ " speed " ] = StringUtil::toString((DWORD)speed);
168 }
169 if (p -> pfp) p -> pfp(msgMap);
170 break ;
171 }
172 if (fp)
173 {
174 fwrite(i_buf.lpvBuffer, sizeof ( char ), i_buf.dwBufferLength, fp);
175 }
176 if (p -> read_content_size > p -> response_content.size())
177 {
178 p -> response_content.append(( char * )i_buf.lpvBuffer, i_buf.dwBufferLength);
179 }
180 p -> dwContentLength += i_buf.dwBufferLength;
181 }
182 break ;
183 }
184
185 if (fp)
186 {
187 fflush(fp); fclose(fp); fp = NULL;
188 }
189
190 if (p -> hFile)
191 {
192 InternetCloseHandle(p -> hFile); // 关闭 m_hFile
193 while ( ! WaitExitEvent(p)) // 等待句柄被关闭事件或者要求子线程退出事件
194 {
195 ResetEvent(p -> hEvent[ 0 ]);
196 }
197 }
198
199 // 设置子线程退出事件,通知回调线程退出
200 SetEvent(p -> hEvent[ 2 ]);
201
202 // 等待回调线程安全退出
203 // WaitForSingleObject(p->hCallbackThread, INFINITE);
204 // CloseHandle(p->hCallbackThread);
205
206 // 注销回调函数
207 InternetSetStatusCallback(p -> hInternet, NULL);
208 InternetCloseHandle(p -> hInternet);
209
210 return TRUE;
211 }
212
213 // ------------------------------------------------------------------------------------
214 DWORD WINAPI AsyncWinINet::AsyncCallbackThread(LPVOID lpParameter)
215 {
216 thread_info * p = (thread_info * )lpParameter;
217 InternetSetStatusCallback(p -> hInternet, AsyncWinINet::AsyncInternetCallback);
218
219 // 通知子线程回调函数设置成功,子线程可以继续工作
220 SetEvent(p -> hEvent[ 0 ]);
221
222 // 等待用户终止事件或者子线程结束事件
223 // 子线程结束前需要设置子线程结束事件,并等待回调线程结束
224 WaitForSingleObject(p -> hEvent[ 2 ], INFINITE);
225
226 return 0 ;
227 }
228
229 // ----------------------------------------------------------------------------
230 VOID CALLBACK AsyncWinINet::AsyncInternetCallback(HINTERNET hInternet,
231 DWORD dwContext,
232 DWORD dwInternetStatus,
233 LPVOID lpvStatusInformation,
234 DWORD dwStatusInformationLength)
235 {
236 thread_info * p = (thread_info * )dwContext;
237
238 // 在我们的应用中,我们只关心下面三个状态
239 switch (dwInternetStatus)
240 {
241 // 句柄被创建
242 case INTERNET_STATUS_HANDLE_CREATED:
243 p -> hFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)
244 (lpvStatusInformation)) -> dwResult);
245 break ;
246
247 // 句柄被关闭
248 case INTERNET_STATUS_HANDLE_CLOSING:
249 SetEvent(p -> hEvent[ 1 ]);
250 break ;
251
252 // 一个请求完成,比如一次句柄创建的请求,或者一次读数据的请求
253 case INTERNET_STATUS_REQUEST_COMPLETE:
254 if (ERROR_SUCCESS == ((LPINTERNET_ASYNC_RESULT)
255 (lpvStatusInformation)) -> dwError)
256 {
257 // 设置句柄被创建事件或者读数据成功完成事件
258 SetEvent(p -> hEvent[ 0 ]);
259 }
260 else
261 {
262 // 如果发生错误,则设置子线程退出事件 这里也是一个陷阱,经常会忽视处理这个错误,
263 SetEvent(p -> hEvent[ 2 ]);
264 }
265 break ;
266
267 case INTERNET_STATUS_CONNECTION_CLOSED:
268 SetEvent(p -> hEvent[ 2 ]);
269 break ;
270
271 }
272 }
273
274 // --------------------------------------------------------------------
275 BOOL AsyncWinINet::WaitExitEvent(thread_info * p)
276 {
277 DWORD dwRet = WaitForMultipleObjects( 3 , p -> hEvent, FALSE, INFINITE);
278
279 switch (dwRet)
280 {
281 case WAIT_OBJECT_0: // 句柄被创建事件或者读数据请求成功完成事件
282 case WAIT_OBJECT_0 + 1 : // 句柄被关闭事件
283 case WAIT_OBJECT_0 + 2 : // 用户要求终止子线程事件或者发生错误事件
284 break ;
285 }
286 return WAIT_OBJECT_0 != dwRet;
287 }
288