WInINet编程中如何使用异步
2016-01-25 10:15
204 查看
在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平台
本文为转载,如有侵权,请联系我:534624117@qq.com,原文地址:http://www.cppblog.com/zhangyq/archive/2009/05/17/83191.aspx
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(); } } } }参考的异步类:
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
#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 }
本文为转载,如有侵权,请联系我:534624117@qq.com,原文地址:http://www.cppblog.com/zhangyq/archive/2009/05/17/83191.aspx
相关文章推荐
- VC++ 用WinInet发送HTTP请求和上传文件
- WinInet
- 几个WinInet函数
- 一个利用WinInet通信的简单客户端Demo
- 常用WinInet API函数简介
- 通过WinInet上传文件
- WinInet API
- wininet api函数使用经验点滴
- 利用MFC的Wininet以http方式下载小文件
- 通过wininet获取网页源码在例子
- 使用WinINet和WinHTTP实现Http访问
- 用Wininet以Https的post方式登录
- wininet的使用
- 解决Android Studio 或eclipse运行时出现Duplicate files copied in APK main/AndroidManifest.xml
- springMvc和shiro整合,shiro的realm不能自动注入的问题
- spring 简单入门实例
- [c++]继承覆盖与函数重载
- C# 注销显示主窗体
- 使用Spring注解注入属性