WinHttp异步实现GET, POST, 多线程下载文件

[复制链接]
发表于 2026-1-15 03:03:31 | 显示全部楼层 |阅读模式
CWinHttpClient.h
  1. #pragma once
  2. #include <Windows.h>
  3. #include <WinHttp.h>
  4. #include <stdint.h>
  5. #include <string>
  6. #include <vector>
  7. #include <functional>
  8. #include <map>
  9. #include <set>
  10. #include <thread>
  11. #include <time.h>
  12. #include <tchar.h>
  13. #ifdef _UNICODE
  14. using _tstring = std::wstring;
  15. #else
  16. using _tstring = std::string;
  17. #endif
  18. #pragma comment(lib, "winhttp.lib")
  19. #define WINHTTP_AGENT_HTTPS "(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0)"
  20. // 响应结果
  21. class CWinHttpResult
  22. {
  23. public:
  24.     CWinHttpResult() :code(0) {}
  25.     std::string result;             //响应结果
  26.     uint32_t code;                  //响应状态码
  27. };
  28. // 进度信息
  29. typedef struct _WINHTTP_PROGRESS_INFO
  30. {
  31.     ULONGLONG ullCur;           // 当前下载量(字节)
  32.     ULONGLONG ullTotal;         // 总数据量(字节)
  33.     double lfProgress;          // 当前进度(0.0f - 1.0f)
  34.     double lfSpeed;             // 当前速度(字节/秒)
  35.     double lfRemainTime;        // 剩余时间(毫秒)
  36.     clock_t costTime;           // 消耗时间(毫秒)
  37.     uint32_t nActiveThread;     // 活动任务数
  38.     uint32_t nTotalThread;      // 总任务线程数
  39. }WINHTTP_PROGRESS_INFO, *LPWINHTTP_PROGRESS_INFO;
  40. // 异步数据
  41. typedef struct _WHTTP_ASYNC_DATA
  42. {
  43.     DWORD_PTR dwContext;                // 上下文
  44.     HANDLE hEvent;                      // 事件
  45.     DWORD dwWait;                       // 事件等待结果
  46.     DWORD dwSize;                       // 传输数据大小
  47.     WINHTTP_ASYNC_RESULT AsyncResult;   // 异步结果
  48. }WHTTP_ASYNC_DATA, *LPWHTTP_ASYNC_DATA;
  49. // 进度回调
  50. using WinHttpCallback = std::function<bool(const WINHTTP_PROGRESS_INFO& progress)>;
  51. class CWinHttpValue;
  52. using CWinHttpObject = std::map<_tstring, CWinHttpValue>;
  53. using CWinHttpArray = std::vector<CWinHttpValue>;
  54. // WinHttp客户端参数类
  55. class CWinHttpValue
  56. {
  57. public:
  58.     CWinHttpValue();
  59.     CWinHttpValue(bool val);
  60.     CWinHttpValue(int val);
  61.     CWinHttpValue(double val);
  62.     CWinHttpValue(const LPCTSTR val);
  63.     CWinHttpValue(const _tstring& val);
  64.     CWinHttpValue(const CWinHttpObject& val);
  65.     CWinHttpValue(const CWinHttpValue& val);
  66.     CWinHttpValue(const CWinHttpArray& val);
  67.     CWinHttpValue(CWinHttpValue&& val) noexcept;
  68.     ~CWinHttpValue();
  69.     bool IsEmpty() const;
  70.     _tstring AsGetString() const;
  71.     _tstring AsJsonString() const;
  72.     _tstring AsHeaderString() const;
  73. private:
  74.     _tstring _AsGetString() const;
  75.     _tstring _AsJsonString() const;
  76.     _tstring _AsHeaderString(int depth) const;
  77.     static void _DumpString(_tstring& append_str, const _tstring& text, bool flag_escape);
  78.     static bool _GetUnicodeString(_tstring& append_str, const TCHAR* data_ptr, const TCHAR** end_ptr);
  79. private:
  80.     union _WIN_HTTP_VALUE_DATA
  81.     {
  82.         bool                _Bool;
  83.         int                 _Int;
  84.         double              _Float;
  85.         _tstring*           _StringPtr;
  86.         CWinHttpObject*     _ObjectPtr;
  87.         CWinHttpArray*      _ArrayPtr;
  88.     }m_Data;
  89.     enum _WIN_HTTP_VALUE_TPYE
  90.     {
  91.         TypeNull,
  92.         TypeBool,
  93.         TypeInt,
  94.         TypeFloat,
  95.         TpyeString,
  96.         TpyeObject,
  97.         TpyeArray
  98.     }m_Type;
  99. };
  100. // WinHttp客户端辅助类
  101. class CWinHttpClient
  102. {
  103. public:
  104.     CWinHttpClient();
  105.     ~CWinHttpClient();
  106.     // 关闭
  107.     void Close();
  108.     // 发送 GET 请求
  109.     CWinHttpResult Get(const _tstring& url, const CWinHttpValue& param = {}, const CWinHttpValue& header = {}, WinHttpCallback cb = nullptr);
  110.     // 发送 POST 请求
  111.     CWinHttpResult Post(const _tstring& url, const CWinHttpValue& param = {}, const CWinHttpValue& header = {}, WinHttpCallback cb = nullptr);
  112.     // 发送 GET 请求(多线程)
  113.     CWinHttpResult GetEx(const _tstring& url, const CWinHttpValue& param = {}, const CWinHttpValue& header = {}, WinHttpCallback cb = nullptr, DWORD dwThreadCount = 1);
  114.     // 多线程下载文件
  115.     CWinHttpResult DownloadFile(const _tstring& url, const _tstring& path = _T(""), const CWinHttpValue& header = {}, WinHttpCallback cb = nullptr, DWORD dwThreadCount = 4);
  116.     // 设置用户代理字符串
  117.     void SetAgent(const _tstring strAgent = _T(WINHTTP_AGENT_HTTPS));
  118.     // 设置请求数据范围
  119.     void SetReQuestDataRange(ULONGLONG ullStart = 0, ULONGLONG ullLength = -1);
  120.     // 设置异步状态打印
  121.     void SetPrintStatus(bool fEnable = true);
  122. public:
  123.     // 字符串转UTF-8编码字符串
  124.     static std::string TStrToU8Str(const _tstring& str);
  125.     // 字符串转宽字节字符串
  126.     static std::wstring TStrToWStr(const _tstring& str);
  127.     // 宽字节字符串转字符串
  128.     static _tstring WStrToTStr(const std::wstring& str);
  129.     // 控制台打印
  130.     static void ConsoleOutput(LPCTSTR pFormat, ...);
  131.     // 编码Utf8字符串
  132.     static std::string EncoderFromUtf8(const std::string& strContent);
  133.     // 解码Utf8字符串
  134.     static std::string DecoderFromUtf8(const std::string& strContent);
  135. private:
  136.     // 发送 GET 请求
  137.     CWinHttpResult _Get(const _tstring& url, WinHttpCallback cb = nullptr);
  138.     // 发送 POST 请求
  139.     CWinHttpResult _Post(const _tstring& url, WinHttpCallback cb = nullptr);
  140.     // 执行请求
  141.     CWinHttpResult _DoRequest(const _tstring& url, const _tstring& strMethod, const std::string& strParam);
  142.     // 执行请求(多线程)
  143.     CWinHttpResult _GetEx(const _tstring& url, WinHttpCallback cb = nullptr, DWORD dwThreadCount = 1);
  144.     CWinHttpResult _MultiThreadRequest(const _tstring& url, ULONGLONG ullContentLength, DWORD dwThreadCount = 1);
  145.     // 读取网络流
  146.     bool _InternetReadData(std::string& strData);
  147.     // 查询资源大小
  148.     bool _QueryContentLength(PULONGLONG lpUllContentLength);
  149.     // 查询是否支持接收范围
  150.     bool _IsSupportAcceptRanges();
  151.     // 设置请求数据范围
  152.     bool _SetRequestDataRange(LONGLONG nBegin, LONGLONG nLength);
  153.     // 获取状态码
  154.     DWORD _GetStatusCode(HINTERNET hRequest);
  155.     // 错误输出
  156.     void _PrintError(LPCTSTR lpszError) const;
  157.     // 状态码打印
  158.     static void _PrintStatus(DWORD dwCode, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength);
  159.     // 状态回调
  160.     static VOID CALLBACK WinHttpStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwStatus, LPVOID lpvInfo, DWORD dwInfoLength);
  161.     // 状态回调
  162.     void _WinHttpStatusCallback(HINTERNET hInternet, DWORD dwStatus, LPVOID lpvInfo, DWORD dwInfoLength);
  163.     // 等待异步事件
  164.     bool _WaitForAsyncEvent(DWORD dwMilliseconds = INFINITE);
  165.     // 打开会话
  166.     bool _WinHttpOpen(LPCWSTR pszAgentW, DWORD dwAccessType, LPCWSTR pszProxyW, LPCWSTR pszProxyBypassW, DWORD dwFlags);
  167.     // 设置会话超时
  168.     bool _WinHttpSetSessionTimeouts(int nResolveTimeout, int nConnectTimeout,int nSendTimeout,int nReceiveTimeout);
  169.     // 设置请求超时
  170.     bool _WinHttpSetRequestTimeouts(int nResolveTimeout, int nConnectTimeout,int nSendTimeout,int nReceiveTimeout);
  171.     // 设置状态回调
  172.     bool _WinHttpSetStatusCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags);
  173.     // 设置会话选项
  174.     bool _WinHttpSetSessionOption(DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength);
  175.     // 设置请求选项
  176.     bool _WinHttpSetRequestOption(DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength);
  177.     // 连接会话
  178.     bool _WinHttpConnect(LPCWSTR pswzServerName, INTERNET_PORT nServerPort);
  179.     // 打开请求
  180.     bool _WinHttpOpenRequest(const _tstring& strVerb, LPCWSTR pwszObjectName, LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR *ppwszAcceptTypes, DWORD dwFlags);
  181.     // 发送请求
  182.     bool _WinHttpSendRequest(_tstring strHeader, LPVOID lpData, DWORD dwSize, DWORD_PTR dwContext);
  183.     // 查询请求头
  184.     bool _WinHttpQueryHeaders(DWORD dwInfoLevel, LPCWSTR pwszName, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex);
  185.     // 接收响应数据
  186.     bool _WinHttpReceiveResponse(HINTERNET hRequest);
  187.     // 查询请求数据是否可用
  188.     bool _WinHttpQueryDataAvailable(LPDWORD lpdwNumberOfBytesAvailable);
  189.     // 读取请求数据
  190.     bool _WinHttpReadData(LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead);
  191.     // 宽字节字符串转多字节字符串
  192.     static std::string _WStrToMultiStr(UINT CodePage, const std::wstring& str);
  193.     // 多字节字符串转宽字节字符串
  194.     static std::wstring _MultiStrToWStr(UINT CodePage, const std::string& str);
  195. private:
  196.     HINTERNET m_hSession;                           // 会话句柄
  197.     HINTERNET m_hConnect;                           // 连接句柄
  198.     HINTERNET m_hRequest;                           // 请求句柄
  199.     WinHttpCallback m_cbProgress;                   // 进度回调
  200.     WHTTP_ASYNC_DATA        m_AsyncData;            // 异步信息
  201.     _tstring                m_strAgent;             // 代理字符串
  202.     _tstring                m_strFilePath;          // 保存文件路径
  203.     _tstring                m_strUrl;               // 请求链接
  204.     _tstring                m_strHeader;            // 请求头信息
  205.     _tstring                m_strParam;             // 请求参数
  206.     ULONGLONG               m_ullStart;             // 请求数据起始位置
  207.     ULONGLONG               m_ullLength;            // 请求长度
  208.     ULONGLONG               m_ullDownload;          // 已下载数据
  209.     bool                    m_fAbort;               // 终止
  210.     bool                    m_fPrint;               // 打印进度
  211. };
复制代码
CWinHttpClient.cpp
  1. #include "CWinHttpClient.h"
  2. #include <strsafe.h>
  3. #include <thread>
  4. #include <atomic>
  5. #include <map>
  6. std::map<int32_t, std::string> g_utf8Code;
  7. static std::map<int32_t, std::string> GetUtf8Code();
  8. typedef struct _WINHTTP_URL_INFO
  9. {
  10.     std::wstring strScheme;
  11.     std::wstring strHostName;
  12.     std::wstring strUserName;
  13.     std::wstring strPassword;
  14.     std::wstring urlPath;
  15.     std::wstring strExtraInfo;
  16.     URL_COMPONENTS uc = { 0 };
  17.     _WINHTTP_URL_INFO()
  18.     {
  19.         memset(&uc, 0, sizeof(uc));
  20.         try
  21.         {
  22.             strScheme.resize(32);
  23.             strHostName.resize(128);
  24.             strUserName.resize(128);
  25.             strPassword.resize(128);
  26.             urlPath.resize(2048);
  27.             strExtraInfo.resize(512);
  28.             this->uc.dwStructSize = sizeof(this->uc);
  29.             this->uc.lpszScheme = &this->strScheme[0];
  30.             this->uc.dwSchemeLength = (DWORD)strScheme.size();
  31.             this->uc.lpszHostName = &this->strHostName[0];
  32.             this->uc.dwHostNameLength = (DWORD)strHostName.size();
  33.             this->uc.lpszUserName = &this->strUserName[0];
  34.             this->uc.dwUserNameLength = (DWORD)strUserName.size();
  35.             this->uc.lpszPassword = &this->strPassword[0];
  36.             this->uc.dwPasswordLength = (DWORD)strPassword.size();
  37.             this->uc.lpszUrlPath = &this->urlPath[0];
  38.             this->uc.dwUrlPathLength = (DWORD)urlPath.size();
  39.             this->uc.lpszExtraInfo = &this->strExtraInfo[0];
  40.             this->uc.dwExtraInfoLength = (DWORD)strExtraInfo.size();
  41.         }
  42.         catch (...)
  43.         {
  44.         }
  45.     }
  46. }WINHTTP_URL_INFO, *PWINHTTP_URL_INFO;
  47. CWinHttpValue::CWinHttpValue()
  48.     :
  49.     m_Data{ 0 },
  50.     m_Type(_WIN_HTTP_VALUE_TPYE::TypeNull)
  51. {
  52. }
  53. CWinHttpValue::CWinHttpValue(bool val) : CWinHttpValue()
  54. {
  55.     m_Data._Bool = val;
  56.     m_Type = _WIN_HTTP_VALUE_TPYE::TypeBool;
  57. }
  58. CWinHttpValue::CWinHttpValue(int val) : CWinHttpValue()
  59. {
  60.     m_Data._Int = val;
  61.     m_Type = _WIN_HTTP_VALUE_TPYE::TypeInt;
  62. }
  63. CWinHttpValue::CWinHttpValue(double val) : CWinHttpValue()
  64. {
  65.     m_Data._Float = val;
  66.     m_Type = _WIN_HTTP_VALUE_TPYE::TypeFloat;
  67. }
  68. CWinHttpValue::CWinHttpValue(const LPCTSTR val) : CWinHttpValue()
  69. {
  70.     if (val)
  71.     {
  72.         m_Data._StringPtr = new (std::nothrow) _tstring(val);
  73.     }
  74.     m_Type = _WIN_HTTP_VALUE_TPYE::TpyeString;
  75. }
  76. CWinHttpValue::CWinHttpValue(const _tstring& val) : CWinHttpValue()
  77. {
  78.     m_Data._StringPtr = new (std::nothrow) _tstring(val);
  79.     m_Type = _WIN_HTTP_VALUE_TPYE::TpyeString;
  80. }
  81. CWinHttpValue::CWinHttpValue(const CWinHttpObject& val) : CWinHttpValue()
  82. {
  83.     m_Data._ObjectPtr = new (std::nothrow) CWinHttpObject(val);
  84.     m_Type = _WIN_HTTP_VALUE_TPYE::TpyeObject;
  85. }
  86. CWinHttpValue::CWinHttpValue(const CWinHttpArray& val) : CWinHttpValue()
  87. {
  88.     m_Data._ArrayPtr = new (std::nothrow) CWinHttpArray(val);
  89.     m_Type = _WIN_HTTP_VALUE_TPYE::TpyeArray;
  90. }
  91. CWinHttpValue::CWinHttpValue(const CWinHttpValue& val) : CWinHttpValue()
  92. {
  93.     if (_WIN_HTTP_VALUE_TPYE::TpyeString == val.m_Type && val.m_Data._StringPtr)
  94.     {
  95.         m_Data._StringPtr = new (std::nothrow) _tstring(*val.m_Data._StringPtr);
  96.     }
  97.     else if (_WIN_HTTP_VALUE_TPYE::TpyeObject == val.m_Type && val.m_Data._ObjectPtr)
  98.     {
  99.         m_Data._ObjectPtr = new (std::nothrow) CWinHttpObject(*val.m_Data._ObjectPtr);
  100.     }
  101.     else if (_WIN_HTTP_VALUE_TPYE::TpyeArray == val.m_Type && val.m_Data._ArrayPtr)
  102.     {
  103.         m_Data._ArrayPtr = new (std::nothrow) CWinHttpArray(*val.m_Data._ArrayPtr);
  104.     }
  105.     m_Type = val.m_Type;
  106. }
  107. CWinHttpValue::CWinHttpValue(CWinHttpValue&& val) noexcept
  108. {
  109.     m_Data = val.m_Data;
  110.     m_Type = val.m_Type;
  111.     val.m_Data = { 0 };
  112.     val.m_Type = _WIN_HTTP_VALUE_TPYE::TypeBool;
  113. }
  114. CWinHttpValue::~CWinHttpValue()
  115. {
  116.     if (_WIN_HTTP_VALUE_TPYE::TpyeString == m_Type)
  117.     {
  118.         delete m_Data._StringPtr;
  119.     }
  120.     else if (_WIN_HTTP_VALUE_TPYE::TpyeObject == m_Type)
  121.     {
  122.         delete m_Data._ObjectPtr;
  123.     }
  124.     else if (_WIN_HTTP_VALUE_TPYE::TpyeArray == m_Type)
  125.     {
  126.         delete m_Data._ArrayPtr;
  127.     }
  128.     m_Data = { 0 };
  129.     m_Type = _WIN_HTTP_VALUE_TPYE::TypeBool;
  130. }
  131. bool CWinHttpValue::IsEmpty() const
  132. {
  133.     if (_WIN_HTTP_VALUE_TPYE::TpyeObject == m_Type && m_Data._ObjectPtr)
  134.     {
  135.         return m_Data._ObjectPtr->empty();
  136.     }
  137.     return false;
  138. }
  139. _tstring CWinHttpValue::AsGetString() const
  140. {
  141.     _tstring strResult;
  142.     if (IsEmpty())
  143.     {
  144.         return strResult;
  145.     }
  146.     return _AsGetString();
  147. }
  148. _tstring CWinHttpValue::AsJsonString() const
  149. {
  150.     return _AsJsonString();
  151. }
  152. _tstring CWinHttpValue::AsHeaderString() const
  153. {
  154.     return _AsHeaderString(0);
  155. }
  156. bool CWinHttpValue::_GetUnicodeString(_tstring& append_str, const TCHAR* data_ptr, const TCHAR** end_ptr)
  157. {
  158.     TCHAR ch = *data_ptr;
  159. #ifdef _UNICODE
  160.     _tchar text_buffer[32] = { 0 };
  161.     _json_stprintf_s(text_buffer, sizeof(text_buffer) / sizeof(_tchar), _T(R"(\u%.4x)"), ch);
  162.     append_str += text_buffer;
  163.     data_ptr++;
  164. #else
  165.     if (ch >= 0xC0)
  166.     {
  167.         // The number of bytes used to obtain characters.
  168.         size_t byte_count = 0;
  169.         uint32_t cp32 = 0;
  170.         if (ch >= 0xE0 && ch <= 0xEF)
  171.         {
  172.             byte_count = 3;
  173.             cp32 = ch & 0x0F;
  174.         }
  175.         else if (ch >= 0xC0 && ch <= 0xDF)
  176.         {
  177.             byte_count = 2;
  178.             cp32 = ch & 0x1F;
  179.         }
  180.         else if (ch >= 0xF0 && ch <= 0xF7)
  181.         {
  182.             byte_count = 4;
  183.             cp32 = ch & 0x07;
  184.         }
  185.         else if (ch >= 0xF8 && ch <= 0xFB)
  186.         {
  187.             byte_count = 5;
  188.             cp32 = ch & 0x03;
  189.         }
  190.         else if (ch >= 0xFC && ch <= 0xFD)
  191.         {
  192.             byte_count = 6;
  193.             cp32 = ch & 0x01;
  194.         }
  195.         if (0 == byte_count)
  196.         {
  197.             return false;
  198.         }
  199.         for (size_t i = 1; i < byte_count; i++)
  200.         {
  201.             cp32 = cp32 << 6;
  202.             cp32 |= data_ptr[i] & 0x3F;
  203.         }
  204.         char text_buffer[32] = { 0 };
  205.         if (cp32 < 0x10000)
  206.         {
  207.             snprintf(text_buffer, sizeof(text_buffer), R"(\u%.4x)", cp32);
  208.             append_str.append(text_buffer, 6);
  209.         }
  210.         else
  211.         {
  212.             uint32_t cp = (uint16_t)(cp32 - 0x10000);
  213.             uint16_t cp32_high = (uint16_t)(cp >> 10) + 0xD800;
  214.             uint16_t cp32_low = (uint16_t)(cp & 0x3FF) + 0xDC00;
  215.             snprintf(text_buffer, sizeof(text_buffer), R"(\u%.4x\u%.4x)", cp32_high, cp32_low);
  216.             append_str.append(text_buffer, 12);
  217.         }
  218.         data_ptr += byte_count;
  219.     }
  220. #endif
  221.     if (end_ptr)
  222.     {
  223.         *end_ptr = data_ptr;
  224.     }
  225.     return false;
  226. }
  227. void CWinHttpValue::_DumpString(_tstring& append_str, const _tstring& text, bool flag_escape)
  228. {
  229.     const TCHAR* data_ptr = text.c_str();
  230.     while (_T('\0') != *data_ptr)
  231.     {
  232.         TCHAR ch = *data_ptr;
  233.         switch (ch)
  234.         {
  235.         case _T('\b'):
  236.         {
  237.             append_str += _T(R"(\b)");
  238.         }
  239.         break;
  240.         case _T('\t'):
  241.         {
  242.             append_str += _T(R"(\t)");
  243.         }
  244.         break;
  245.         case _T('\n'):
  246.         {
  247.             append_str += _T(R"(\n)");
  248.         }
  249.         break;
  250.         case _T('\f'):
  251.         {
  252.             append_str += _T(R"(\f)");
  253.         }
  254.         break;
  255.         case _T('\r'):
  256.         {
  257.             append_str += _T(R"(\r)");
  258.         }
  259.         break;
  260.         case _T('"'):
  261.         {
  262.             append_str += _T(R"(")");
  263.         }
  264.         break;
  265.         case _T('/'):
  266.         {
  267.             append_str += _T(R"(/)");
  268.         }
  269.         case _T('\\'):
  270.         {
  271.             append_str += _T(R"(\\)");
  272.         }
  273.         break;
  274.         default:
  275.         {
  276.             if (ch < 0x80 || !flag_escape)
  277.             {
  278.                 append_str.push_back(ch);
  279.             }
  280.             else
  281.             {
  282.                 _GetUnicodeString(append_str, data_ptr, &data_ptr);
  283.                 continue;
  284.             }
  285.         }
  286.         break;
  287.         }
  288.         data_ptr++;
  289.     }
  290. }
  291. _tstring CWinHttpValue::_AsGetString() const
  292. {
  293.     _tstring strResult;
  294.     if (_WIN_HTTP_VALUE_TPYE::TypeBool == m_Type)
  295.     {
  296.         strResult = m_Data._Bool ? _T("true") : _T("false");
  297.     }
  298.     else if (_WIN_HTTP_VALUE_TPYE::TypeInt == m_Type)
  299.     {
  300.         TCHAR szBuf[MAX_PATH] = { 0 };
  301.         ::StringCchPrintf(szBuf, _countof(szBuf), _T("%d"), m_Data._Int);
  302.         strResult = szBuf;
  303.     }
  304.     else if (_WIN_HTTP_VALUE_TPYE::TypeFloat == m_Type)
  305.     {
  306.         TCHAR szBuf[MAX_PATH] = { 0 };
  307.         ::StringCchPrintf(szBuf, _countof(szBuf), _T("%.16g"), m_Data._Float);
  308.         strResult = szBuf;
  309.         TCHAR* chPtr = szBuf;
  310.         bool flag_dot = false;
  311.         bool flag_exponent = false;
  312.         while (_T('\0') != *chPtr)
  313.         {
  314.             TCHAR ch = *chPtr;
  315.             if (_T('.') == ch)
  316.             {
  317.                 flag_dot = true;
  318.             }
  319.             else if (_T('e') == ch)
  320.             {
  321.                 flag_exponent = true;
  322.             }
  323.             chPtr++;
  324.         }
  325.         if (!flag_dot && 0 == !flag_exponent)
  326.         {
  327.             strResult += _T(".0");
  328.         }
  329.     }
  330.     else if (_WIN_HTTP_VALUE_TPYE::TpyeString == m_Type)
  331.     {
  332.         strResult = *m_Data._StringPtr;
  333.     }
  334.     else if (_WIN_HTTP_VALUE_TPYE::TpyeObject == m_Type)
  335.     {
  336.         const CWinHttpObject& object = *m_Data._ObjectPtr;
  337.         size_t size = object.size();
  338.         if (!object.empty())
  339.         {
  340.             strResult += _T("?");
  341.             for (const auto& item : object)
  342.             {
  343.                 strResult += item.first;
  344.                 strResult += _T("=");
  345.                 strResult += item.second._AsGetString();
  346.                 size--;
  347.                 if (size > 0)
  348.                 {
  349.                     strResult += _T("&");
  350.                 }
  351.             }
  352.         }
  353.     }
  354.     else if (_WIN_HTTP_VALUE_TPYE::TpyeArray == m_Type)
  355.     {
  356.         const CWinHttpArray& object = *m_Data._ArrayPtr;
  357.         size_t size = object.size();
  358.         for (const auto& item : object)
  359.         {
  360.             strResult += item._AsGetString();
  361.             size--;
  362.             if (size > 0)
  363.             {
  364.                 strResult += _T(",");
  365.             }
  366.         }
  367.     }
  368.     return strResult;
  369. }
  370. _tstring CWinHttpValue::_AsJsonString() const
  371. {
  372.     _tstring strResult;
  373.     if (_WIN_HTTP_VALUE_TPYE::TypeBool == m_Type)
  374.     {
  375.         strResult = m_Data._Bool ? _T("true") : _T("false");
  376.     }
  377.     else if (_WIN_HTTP_VALUE_TPYE::TypeInt == m_Type)
  378.     {
  379.         TCHAR szBuf[MAX_PATH] = { 0 };
  380.         ::StringCchPrintf(szBuf, _countof(szBuf), _T("%d"), m_Data._Int);
  381.         strResult = szBuf;
  382.     }
  383.     else if (_WIN_HTTP_VALUE_TPYE::TypeFloat == m_Type)
  384.     {
  385.         TCHAR szBuf[MAX_PATH] = { 0 };
  386.         ::StringCchPrintf(szBuf, _countof(szBuf), _T("%.16g"), m_Data._Float);
  387.         strResult = szBuf;
  388.         TCHAR* chPtr = szBuf;
  389.         bool flag_dot = false;
  390.         bool flag_exponent = false;
  391.         while (_T('\0') != *chPtr)
  392.         {
  393.             TCHAR ch = *chPtr;
  394.             if (_T('.') == ch)
  395.             {
  396.                 flag_dot = true;
  397.             }
  398.             else if (_T('e') == ch)
  399.             {
  400.                 flag_exponent = true;
  401.             }
  402.             chPtr++;
  403.         }
  404.         if (!flag_dot && 0 == !flag_exponent)
  405.         {
  406.             strResult += _T(".0");
  407.         }
  408.     }
  409.     else if (_WIN_HTTP_VALUE_TPYE::TpyeString == m_Type)
  410.     {
  411.         strResult += _T(""");
  412.         _DumpString(strResult, *m_Data._StringPtr, false);
  413.         strResult += _T(""");
  414.     }
  415.     else if (_WIN_HTTP_VALUE_TPYE::TpyeObject == m_Type)
  416.     {
  417.         const CWinHttpObject& object = *m_Data._ObjectPtr;
  418.         size_t size = object.size();
  419.         if (!object.empty())
  420.         {
  421.             strResult += _T("{");
  422.             for (const auto& item : object)
  423.             {
  424.                 strResult += _T(""");
  425.                 _DumpString(strResult, item.first, false);
  426.                 strResult += _T(""");
  427.                 strResult += _T(":");
  428.                 strResult += item.second._AsJsonString();
  429.                 size--;
  430.                 if (size > 0)
  431.                 {
  432.                     strResult += _T(",");
  433.                 }
  434.             }
  435.             strResult += _T("}");
  436.         }
  437.     }
  438.     else if (_WIN_HTTP_VALUE_TPYE::TpyeArray == m_Type)
  439.     {
  440.         const CWinHttpArray& object = *m_Data._ArrayPtr;
  441.         size_t size = object.size();
  442.         strResult += _T("[");
  443.         for (const auto& item : object)
  444.         {
  445.             strResult += item._AsJsonString();
  446.             size--;
  447.             if (size > 0)
  448.             {
  449.                 strResult += _T(",");
  450.             }
  451.         }
  452.         strResult += _T("]");
  453.     }
  454.     return strResult;
  455. }
  456. _tstring CWinHttpValue::_AsHeaderString(int depth) const
  457. {
  458.     _tstring strResult;
  459.     if (_WIN_HTTP_VALUE_TPYE::TypeBool == m_Type)
  460.     {
  461.         strResult = m_Data._Bool ? _T("true") : _T("false");
  462.     }
  463.     else if (_WIN_HTTP_VALUE_TPYE::TypeInt == m_Type)
  464.     {
  465.         TCHAR szBuf[MAX_PATH] = { 0 };
  466.         ::StringCchPrintf(szBuf, _countof(szBuf), _T("%d"), m_Data._Int);
  467.         strResult = szBuf;
  468.     }
  469.     else if (_WIN_HTTP_VALUE_TPYE::TypeFloat == m_Type)
  470.     {
  471.         TCHAR szBuf[MAX_PATH] = { 0 };
  472.         ::StringCchPrintf(szBuf, _countof(szBuf), _T("%.16g"), m_Data._Float);
  473.         strResult = szBuf;
  474.         TCHAR* chPtr = szBuf;
  475.         bool flag_dot = false;
  476.         bool flag_exponent = false;
  477.         while (_T('\0') != *chPtr)
  478.         {
  479.             TCHAR ch = *chPtr;
  480.             if (_T('.') == ch)
  481.             {
  482.                 flag_dot = true;
  483.             }
  484.             else if (_T('e') == ch)
  485.             {
  486.                 flag_exponent = true;
  487.             }
  488.             chPtr++;
  489.         }
  490.         if (!flag_dot && 0 == !flag_exponent)
  491.         {
  492.             strResult += _T(".0");
  493.         }
  494.     }
  495.     else if (_WIN_HTTP_VALUE_TPYE::TpyeString == m_Type)
  496.     {
  497.         strResult += *m_Data._StringPtr;
  498.     }
  499.     else if (_WIN_HTTP_VALUE_TPYE::TpyeObject == m_Type)
  500.     {
  501.         const CWinHttpObject& object = *m_Data._ObjectPtr;
  502.         size_t size = object.size();
  503.         if (!object.empty())
  504.         {
  505.             for (const auto& item : object)
  506.             {
  507.                 strResult += item.first;
  508.                 strResult += _T(": ");
  509.                 strResult += item.second._AsHeaderString(depth + 1);
  510.                 size--;
  511.                 if (size > 0)
  512.                 {
  513.                     strResult += _T("\r\n");
  514.                 }
  515.             }
  516.         }
  517.     }
  518.     else if (_WIN_HTTP_VALUE_TPYE::TpyeArray == m_Type)
  519.     {
  520.         const CWinHttpArray& object = *m_Data._ArrayPtr;
  521.         size_t size = object.size();
  522.         for (const auto& item : object)
  523.         {
  524.             strResult += item._AsHeaderString(depth + 1);
  525.             size--;
  526.             if (size > 0)
  527.             {
  528.                 strResult += _T(", ");
  529.             }
  530.         }
  531.     }
  532.     return strResult;
  533. }
  534. CWinHttpClient::CWinHttpClient()
  535.     :
  536.     m_hSession(NULL),
  537.     m_hConnect(NULL),
  538.     m_hRequest(NULL),
  539.     m_fAbort(false),
  540.     m_fPrint(false),
  541.     m_ullStart(0),
  542.     m_ullLength(-1),
  543.     m_ullDownload(0),
  544.     m_AsyncData{ 0 }
  545. {
  546.     m_AsyncData.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  547.     return;
  548. }
  549. CWinHttpClient::~CWinHttpClient()
  550. {
  551.     Close();
  552.     if (NULL != m_AsyncData.hEvent)
  553.     {
  554.         ::CloseHandle(m_AsyncData.hEvent);
  555.     }
  556. }
  557. void CWinHttpClient::Close()
  558. {
  559.     m_fAbort = true;
  560.     if (NULL != m_AsyncData.hEvent)
  561.     {
  562.         ::SetEvent(m_AsyncData.hEvent);
  563.     }
  564.     if (m_hRequest)
  565.     {
  566.         ::WinHttpCloseHandle(m_hRequest);
  567.         m_hRequest = NULL;
  568.     }
  569.     if (m_hConnect)
  570.     {
  571.         ::WinHttpCloseHandle(m_hConnect);
  572.         m_hConnect = NULL;
  573.     }
  574.     if (m_hSession)
  575.     {
  576.         ::WinHttpCloseHandle(m_hSession);
  577.         m_hSession = NULL;
  578.     }
  579.     m_fAbort = false;
  580. }
  581. std::string FormatA(LPCSTR pFormat, ...)
  582. {
  583.     size_t nCchCount = MAX_PATH;
  584.     std::string strResult(nCchCount, 0);
  585.     va_list args;
  586.     va_start(args, pFormat);
  587.     do
  588.     {
  589.         //成功则赋值字符串并终止循环
  590.         int nSize = _vsnprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);
  591.         if (-1 != nSize)
  592.         {
  593.             strResult.resize(nSize);
  594.             break;
  595.         }
  596.         //缓冲大小超限终止
  597.         if (nCchCount >= INT32_MAX)
  598.         {
  599.             break;
  600.         }
  601.         //重新分配缓冲
  602.         nCchCount *= 2;
  603.         strResult.resize(nCchCount);
  604.     } while (true);
  605.     va_end(args);
  606.     return strResult;
  607. }
  608. std::map<int32_t, std::string> GetUtf8Code()
  609. {
  610.     std::map<int32_t, std::string> mapCode;
  611.     uint8_t szBuf[MAX_PATH] = { 0 };
  612.     for (uint32_t i = 0x4E00; i <= 0x9FFF; i++)
  613.     {
  614.         // 1字节
  615.         // 0xxxxxxx
  616.         if (i >= 0x00000000 && i <= 0x0000007F)
  617.         {
  618.             szBuf[0] = i;
  619.             szBuf[1] = 0;
  620.         }
  621.         // 2字节
  622.         // 110xxxxx 10xxxxxx
  623.         if (i >= 0x00000080 && i <= 0x000007FF)
  624.         {
  625.             szBuf[0] = ((i >> 6) & 0x1F) | 0xC0;
  626.             szBuf[1] = ( i & 0x3F) | 0x80;
  627.             szBuf[2] = 0;
  628.         }
  629.         // 3字节
  630.         // 1110xxxx 10xxxxxx 10xxxxxx
  631.         if (i >= 0x00000800 && i <= 0x0000FFFF)
  632.         {
  633.             szBuf[0] = ((i >> 12) & 0x0F) | 0xE0;
  634.             szBuf[1] = ((i >> 6) & 0x3F) | 0x80;
  635.             szBuf[2] = (i & 0x3F) | 0x80;
  636.             szBuf[3] = 0;
  637.         }
  638.         mapCode.insert(std::make_pair(i, (char*)szBuf));
  639.     }
  640.     return mapCode;
  641. }
  642. bool IsUtf8String(const std::string& strContent)
  643. {
  644.     bool fResult = false;
  645.     size_t nLength = strContent.size();
  646.     const char* pStr = strContent.c_str();
  647.     bool fAscii = true;
  648.     int nBytes = 0;
  649.     for (int i = 0; i < nLength; i++)
  650.     {
  651.         char ch = pStr[i];
  652.         if (0 != (ch & 0x80))
  653.         {
  654.             fAscii = false;
  655.         }
  656.         if (ch >= 0x80)
  657.         {
  658.             // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  659.             if (ch >= 0xFC && ch <= 0xFD)
  660.             {
  661.                 nBytes = 6;
  662.             }
  663.             // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  664.             else if (ch >= 0xF8 && ch <= 0xFB)
  665.             {
  666.                 nBytes = 5;
  667.             }
  668.             // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  669.             else if (ch >= 0xF0 && ch <= 0xF7)
  670.             {
  671.                 nBytes = 4;
  672.             }
  673.             // 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  674.             else if (ch >= 0xE0 && ch <= 0xEF)
  675.             {
  676.                 nBytes = 3;
  677.             }
  678.             // 110xxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  679.             else if (ch >= 0xC0 && ch <= 0xDF)
  680.             {
  681.                 nBytes = 2;
  682.             }
  683.             else
  684.             {
  685.                 return FALSE;
  686.             }
  687.             nBytes--;
  688.         }
  689.     }
  690.     return fResult;
  691. }
  692. std::string CWinHttpClient::EncoderFromUtf8(const std::string& strContent)
  693. {
  694.     std::string strResult;
  695.     return strResult;
  696. }
  697. std::string CWinHttpClient::DecoderFromUtf8(const std::string& strContent)
  698. {
  699.     std::string strResult;
  700.     strResult.reserve(strContent.capacity());
  701.     size_t nLength = strContent.size();
  702.     const char* pStr = strContent.c_str();
  703.     for (int i = 0; i < nLength; i++)
  704.     {
  705.         // 转义解码
  706.         if (('\\' == pStr[i]))
  707.         {
  708.             // Unicode 字符
  709.             if ((i + 5) < nLength && 'u' == pStr[i + 1])
  710.             {
  711.                 char szCode[8] = { 0 };
  712.                 szCode[0] = pStr[i + 2];
  713.                 szCode[1] = pStr[i + 3];
  714.                 szCode[2] = pStr[i + 4];
  715.                 szCode[3] = pStr[i + 5];
  716.                 uint32_t uCode = _strtoui64(szCode, nullptr, 16);
  717.                 // 1字节
  718.                 // 0xxxxxxx
  719.                 if (uCode >= 0x00000000 && uCode <= 0x0000007F)
  720.                 {
  721.                     szCode[0] = uCode;
  722.                     szCode[1] = 0;
  723.                 }
  724.                 // 2字节
  725.                 // 110xxxxx 10xxxxxx
  726.                 if (uCode >= 0x00000080 && uCode <= 0x000007FF)
  727.                 {
  728.                     szCode[0] = ((uCode >> 6) & 0x1F) | 0xC0;
  729.                     szCode[1] = ( uCode & 0x3F) | 0x80;
  730.                     szCode[2] = 0;
  731.                 }
  732.                 // 3字节
  733.                 // 1110xxxx 10xxxxxx 10xxxxxx
  734.                 if (uCode >= 0x00000800 && uCode <= 0x0000FFFF)
  735.                 {
  736.                     szCode[0] = ((uCode >> 12) & 0x0F) | 0xE0;
  737.                     szCode[1] = ((uCode >> 6) & 0x3F) | 0x80;
  738.                     szCode[2] = (uCode & 0x3F) | 0x80;
  739.                     szCode[3] = 0;
  740.                 }
  741.                 // 4字节
  742.                 // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  743.                 if (uCode >= 0x00010000 && uCode <= 0x001FFFFF)
  744.                 {
  745.                     szCode[0] = ((uCode >> 18) & 0x07) | 0xF0;
  746.                     szCode[1] = ((uCode >> 12) & 0x3F) | 0x80;
  747.                     szCode[2] = ((uCode >> 6) & 0x3F) | 0x80;
  748.                     szCode[3] = (uCode & 0x3F) | 0x80;
  749.                     szCode[4] = 0;
  750.                 }
  751.                 // 5字节
  752.                 // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  753.                 if (uCode >= 0x00200000 && uCode <= 0x03FFFFFF)
  754.                 {
  755.                     szCode[0] = ((uCode >> 24) & 0x03) | 0xF8;
  756.                     szCode[1] = ((uCode >> 18) & 0x3F) | 0x80;
  757.                     szCode[2] = ((uCode >> 12) & 0x3F) | 0x80;
  758.                     szCode[3] = ((uCode >> 6) & 0x3F) | 0x80;
  759.                     szCode[4] = (uCode & 0x3F) | 0x80;
  760.                     szCode[5] = 0;
  761.                 }
  762.                 // 6字节
  763.                 // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  764.                 if (uCode >= 0x04000000 && uCode <= 0x7FFFFFFF)
  765.                 {
  766.                     szCode[0] = ((uCode >> 30) & 0x01) | 0xFC;
  767.                     szCode[1] = ((uCode >> 24) & 0x3F) | 0x80;
  768.                     szCode[2] = ((uCode >> 18) & 0x3F) | 0x80;
  769.                     szCode[3] = ((uCode >> 12) & 0x3F) | 0x80;
  770.                     szCode[4] = ((uCode >> 6) & 0x3F) | 0x80;
  771.                     szCode[5] = (uCode & 0x3F) | 0x80;
  772.                     szCode[6] = 0;
  773.                 }
  774.                 strResult += szCode;
  775.                 i += 5;
  776.             }
  777.             // 转义字符
  778.             switch (pStr[i + 1])
  779.             {
  780.             case '"':
  781.             {
  782.                 strResult.push_back('"');
  783.                 i += 1;
  784.             }
  785.             break;
  786.             case '/':
  787.             {
  788.                 strResult.push_back('"');
  789.                 i += 1;
  790.             }
  791.             break;
  792.             case 'b':
  793.             {
  794.                 strResult.push_back('\b');
  795.                 i += 1;
  796.             }
  797.             break;
  798.             case 'f':
  799.             {
  800.                 strResult.push_back('\f');
  801.                 i += 1;
  802.             }
  803.             break;
  804.             case 't':
  805.             {
  806.                 strResult.push_back('\t');
  807.                 i += 1;
  808.             }
  809.             break;
  810.             case 'r':
  811.             {
  812.                 strResult.push_back('\r');
  813.                 i += 1;
  814.             }
  815.             break;
  816.             case 'n':
  817.             {
  818.                 strResult.push_back('\n');
  819.                 i += 1;
  820.             }
  821.             break;
  822.             case '<':
  823.             {
  824.                 strResult.push_back('<');
  825.                 i += 1;
  826.             }
  827.             break;
  828.             case '>':
  829.             {
  830.                 strResult.push_back('>');
  831.                 i += 1;
  832.             }
  833.             break;
  834.             case '&':
  835.             {
  836.                 strResult.push_back('&');
  837.                 i += 1;
  838.             }
  839.             break;
  840.             }
  841.         }
  842.         else
  843.         {
  844.             strResult.push_back(pStr[i]);
  845.         }
  846.     }
  847.     strResult.push_back('\0');
  848.     return strResult;
  849. }
  850. static bool _CrackUrl(const _tstring& url, PWINHTTP_URL_INFO lpUrlInfo)
  851. {
  852.     // 将 URL 分解到其组件部件中
  853.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpcrackurl
  854.     // 即使在异步模式下使用 WinHTTP (即在 WinHttpOpen) 中设置了WINHTTP_FLAG_ASYNC时,此函数也会同步运行。
  855.     if (!::WinHttpCrackUrl(CWinHttpClient::TStrToWStr(url).c_str(), 0, 0, &lpUrlInfo->uc))
  856.     {
  857.         return false;
  858.     }
  859.     lpUrlInfo->strScheme.resize(wcslen(lpUrlInfo->strScheme.c_str()));
  860.     lpUrlInfo->strExtraInfo.resize(wcslen(lpUrlInfo->strExtraInfo.c_str()));
  861.     lpUrlInfo->strHostName.resize(wcslen(lpUrlInfo->strHostName.c_str()));
  862.     lpUrlInfo->strUserName.resize(wcslen(lpUrlInfo->strUserName.c_str()));
  863.     lpUrlInfo->strPassword.resize(wcslen(lpUrlInfo->strPassword.c_str()));
  864.     lpUrlInfo->urlPath.resize(wcslen(lpUrlInfo->urlPath.c_str()));
  865.     if (!lpUrlInfo->strExtraInfo.empty())
  866.     {
  867.         lpUrlInfo->urlPath += lpUrlInfo->strExtraInfo;
  868.     }
  869.     // 协议检查
  870.     if (!(INTERNET_SCHEME_HTTPS == lpUrlInfo->uc.nScheme || INTERNET_SCHEME_HTTP == lpUrlInfo->uc.nScheme))
  871.     {
  872.         return false;
  873.     }
  874.     return true;
  875. }
  876. CWinHttpResult CWinHttpClient::Get(const _tstring& url, const CWinHttpValue& param/* = {}*/, const CWinHttpValue& header/* = {}*/, WinHttpCallback cb/* = nullptr*/)
  877. {
  878.     m_strFilePath.clear();
  879.     m_cbProgress = cb;
  880.     m_strUrl = url;
  881.     m_strParam = param.AsGetString();
  882.     m_strHeader = header.AsHeaderString();
  883.     return _Get(url, cb);
  884. }
  885. CWinHttpResult CWinHttpClient::Post(const _tstring& url, const CWinHttpValue& param/* = {}*/, const CWinHttpValue& header/* = {}*/, WinHttpCallback cb/* = nullptr*/)
  886. {
  887.     m_strFilePath.clear();
  888.     m_cbProgress = cb;
  889.     m_strUrl = url;
  890.     m_strParam = param.AsJsonString();
  891.     m_strHeader = header.AsHeaderString();
  892.     return _Post(url, cb);
  893. }
  894. CWinHttpResult CWinHttpClient::GetEx(const _tstring& url, const CWinHttpValue& param/* = {}*/, const CWinHttpValue& header/* = {}*/, WinHttpCallback cb/* = nullptr*/, DWORD dwThreadCount/* = 1*/)
  895. {
  896.     m_strFilePath.clear();
  897.     m_cbProgress = cb;
  898.     m_strUrl = url;
  899.     m_strParam = param.AsJsonString();
  900.     m_strHeader = header.AsHeaderString();
  901.     return _GetEx(url, cb, dwThreadCount);
  902. }
  903. CWinHttpResult CWinHttpClient::DownloadFile(const _tstring& url, const _tstring& strFile, const CWinHttpValue& header/* = {}*/, WinHttpCallback cb/* = nullptr*/, DWORD dwThreadCount/* = 4*/)
  904. {
  905.     m_strFilePath = strFile;
  906.     m_strUrl = url;
  907.     m_strParam.clear();
  908.     m_strHeader = header.AsHeaderString();
  909.     if (m_strFilePath.empty())
  910.     {
  911.         size_t nPos = url.find_last_of(_T("/"));
  912.         if (_tstring::npos != nPos)
  913.         {
  914.             m_strFilePath = url.substr(nPos + 1);
  915.         }
  916.     }
  917.     // 清空文件
  918.     HANDLE hFile = INVALID_HANDLE_VALUE;
  919.     hFile = ::CreateFile(strFile.c_str(),
  920.         GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE,
  921.         NULL,
  922.         TRUNCATE_EXISTING,
  923.         FILE_ATTRIBUTE_ARCHIVE,
  924.         NULL);
  925.     if (INVALID_HANDLE_VALUE != hFile)
  926.     {
  927.         ::CloseHandle(hFile);
  928.     }
  929.     CWinHttpResult httpResult = _GetEx(url, cb, dwThreadCount);
  930.     return httpResult;
  931. }
  932. CWinHttpResult CWinHttpClient::_Get(const _tstring& url, WinHttpCallback cb/* = nullptr*/)
  933. {
  934.     _tstring urlAddr = url;
  935.     if (!m_strParam.empty())
  936.     {
  937.         urlAddr += m_strParam;
  938.     }
  939.     return _DoRequest(urlAddr, _T("GET"), "");
  940. }
  941. CWinHttpResult CWinHttpClient::_Post(const _tstring& url, WinHttpCallback cb/* = nullptr*/)
  942. {
  943.     std::string strParam = TStrToU8Str(m_strParam);
  944.     return _DoRequest(url, _T("POST"), strParam);
  945. }
  946. CWinHttpResult CWinHttpClient::_DoRequest(const _tstring& url, const _tstring& strMethod, const std::string& strParam)
  947. {
  948.     WINHTTP_URL_INFO urlInfo;
  949.     CWinHttpResult httpResult;
  950.     if (!_CrackUrl(url, &urlInfo))
  951.     {
  952.         _PrintError(_T("_CrackUrl"));
  953.         return httpResult;
  954.     }
  955.     do
  956.     {
  957.         // 初始化 HTTP 会话
  958.         std::wstring wstrAgent = TStrToWStr(m_strAgent);
  959.         DWORD dwAccessType = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY;
  960.         LPCWSTR pszProxy = WINHTTP_NO_PROXY_NAME;
  961.         LPCWSTR pszProxyBypass = WINHTTP_NO_PROXY_BYPASS;
  962.         DWORD dwSessionFlags = WINHTTP_FLAG_ASYNC;    //WINHTTP_FLAG_ASYNC / WINHTTP_FLAG_SECURE_DEFAULTS
  963.         if (!_WinHttpOpen(wstrAgent.c_str(), dwAccessType, pszProxy, pszProxyBypass, dwSessionFlags))
  964.         {
  965.             _PrintError(_T("_WinHttpOpen"));
  966.             break;
  967.         }
  968.         // 设置回调函数
  969.         if (!_WinHttpSetStatusCallback(WinHttpStatusCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS))
  970.         {
  971.             _PrintError(_T("WinHttpSetStatusCallback"));
  972.             break;
  973.         }
  974.         // 设置状态回调上下文
  975.         DWORD_PTR dwContext = (DWORD_PTR)this;
  976.         if (!_WinHttpSetSessionOption(WINHTTP_OPTION_CONTEXT_VALUE, &dwContext, sizeof(dwContext)))
  977.         {
  978.             _PrintError(_T("WinHttpSetOption"));
  979.             break;
  980.         }
  981.         // 连接 HTTP 会话
  982.         if (!_WinHttpConnect(urlInfo.strHostName.c_str(), urlInfo.uc.nPort))
  983.         {
  984.             _PrintError(_T("_WinHttpConnect"));
  985.             break;
  986.         }
  987.         // 创建 HTTP 请求句柄
  988.         LPCWSTR pwszReferrer = WINHTTP_NO_REFERER;
  989.         LPCWSTR* pwszAcceptTypes = WINHTTP_DEFAULT_ACCEPT_TYPES;
  990.         DWORD dwRequestFlags = (INTERNET_SCHEME_HTTPS == urlInfo.uc.nScheme) ? WINHTTP_FLAG_SECURE : 0;
  991.         if (!_WinHttpOpenRequest(strMethod, urlInfo.urlPath.c_str(), NULL, pwszReferrer, pwszAcceptTypes, dwRequestFlags))
  992.         {
  993.             _PrintError(_T("_WinHttpOpenRequest"));
  994.             break;
  995.         }
  996.         // 设置请求超时
  997.         if (!_WinHttpSetRequestTimeouts(500, 500, 1000, 1000))
  998.         {
  999.             _PrintError(_T("WinHttpSetTimeouts"));
  1000.             break;
  1001.         }
  1002.         // 设置请求范围
  1003.         if (!_SetRequestDataRange(m_ullStart, m_ullLength))
  1004.         {
  1005.             _PrintError(_T("_SetRequestDataRange"));
  1006.             break;
  1007.         }
  1008.         // 发送请求
  1009.         if (!_WinHttpSendRequest(m_strHeader, (LPVOID)strParam.data(), (DWORD)strParam.size(), (DWORD_PTR)this))
  1010.         {
  1011.             _PrintError(_T("_WinHttpSendRequest"));
  1012.             break;
  1013.         }
  1014.         // 等待接收请求响应
  1015.         if (!_WinHttpReceiveResponse(m_hRequest))
  1016.         {
  1017.             _PrintError(_T("_WinHttpReceiveResponse"));
  1018.             break;
  1019.         }
  1020.         // 查询文件大小
  1021.         ULONGLONG UllContentLength = 0;
  1022.         if (!_QueryContentLength(&UllContentLength))
  1023.         {
  1024.             _PrintError(_T("_QueryContentLength"));
  1025.             break;
  1026.         }
  1027.         // 获取响应码
  1028.         httpResult.code = _GetStatusCode(m_hRequest);
  1029.         if (httpResult.code < 200 || httpResult.code >= 300)
  1030.         {
  1031.             _PrintError(_T("_GetStatusCode"));
  1032.             break;
  1033.         }
  1034.         // 接收请求数据
  1035.         if (!_InternetReadData(httpResult.result))
  1036.         {
  1037.             _PrintError(_T("_InternetReadData"));
  1038.             break;
  1039.         }
  1040.     } while (false);
  1041.     // 关闭相关句柄
  1042.     Close();
  1043.     return httpResult;
  1044. }
  1045. CWinHttpResult CWinHttpClient::_GetEx(const _tstring& url, WinHttpCallback cb/* = nullptr*/, DWORD dwThreadCount/* = 6*/)
  1046. {
  1047.     WINHTTP_URL_INFO urlInfo;
  1048.     CWinHttpResult httpResult;
  1049.     if (!_CrackUrl(url, &urlInfo))
  1050.     {
  1051.         _PrintError(_T("_CrackUrl"));
  1052.         return httpResult;
  1053.     }
  1054.     m_cbProgress = cb;
  1055.     do
  1056.     {
  1057.         // 初始化 HTTP 会话
  1058.         std::wstring wstrAgent = TStrToWStr(m_strAgent);
  1059.         DWORD dwAccessType = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY;
  1060.         LPCWSTR pszProxy = WINHTTP_NO_PROXY_NAME;
  1061.         LPCWSTR pszProxyBypass = WINHTTP_NO_PROXY_BYPASS;
  1062.         DWORD dwSessionFlags = WINHTTP_FLAG_ASYNC;    //WINHTTP_FLAG_ASYNC / WINHTTP_FLAG_SECURE_DEFAULTS
  1063.         if (!_WinHttpOpen(wstrAgent.c_str(), dwAccessType, pszProxy, pszProxyBypass, dwSessionFlags))
  1064.         {
  1065.             _PrintError(_T("_WinHttpOpen"));
  1066.             break;
  1067.         }
  1068.         // 设置回调函数
  1069.         if (!_WinHttpSetStatusCallback(WinHttpStatusCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS))
  1070.         {
  1071.             _PrintError(_T("WinHttpSetStatusCallback"));
  1072.             break;
  1073.         }
  1074.         // 设置状态回调上下文
  1075.         DWORD_PTR dwContext = (DWORD_PTR)this;
  1076.         if (!_WinHttpSetSessionOption(WINHTTP_OPTION_CONTEXT_VALUE, &dwContext, sizeof(dwContext)))
  1077.         {
  1078.             _PrintError(_T("WinHttpSetOption"));
  1079.             break;
  1080.         }
  1081.         // 连接 HTTP 会话
  1082.         if (!_WinHttpConnect(urlInfo.strHostName.c_str(), urlInfo.uc.nPort))
  1083.         {
  1084.             _PrintError(_T("_WinHttpConnect"));
  1085.             break;
  1086.         }
  1087.         // 创建 HTTP 请求句柄
  1088.         LPCWSTR pwszReferrer = WINHTTP_NO_REFERER;
  1089.         LPCWSTR* pwszAcceptTypes = WINHTTP_DEFAULT_ACCEPT_TYPES;
  1090.         DWORD dwRequestFlags = (INTERNET_SCHEME_HTTPS == urlInfo.uc.nScheme) ? WINHTTP_FLAG_SECURE : 0;
  1091.         if (!_WinHttpOpenRequest(_T("GET"), urlInfo.urlPath.c_str(), NULL, pwszReferrer, pwszAcceptTypes, dwRequestFlags))
  1092.         {
  1093.             _PrintError(_T("_WinHttpOpenRequest"));
  1094.             break;
  1095.         }
  1096.         // 设置请求超时
  1097.         if (!_WinHttpSetRequestTimeouts(1000, 1000, 3000, 5000))
  1098.         {
  1099.             _PrintError(_T("WinHttpSetTimeouts"));
  1100.             break;
  1101.         }
  1102.         // 发送请求
  1103.         if (!_WinHttpSendRequest(m_strHeader, NULL, 0, (DWORD_PTR)this))
  1104.         {
  1105.             _PrintError(_T("_WinHttpSendRequest"));
  1106.             break;
  1107.         }
  1108.         // 等待接收请求响应
  1109.         if (!_WinHttpReceiveResponse(m_hRequest))
  1110.         {
  1111.             _PrintError(_T("_WinHttpReceiveResponse"));
  1112.             break;
  1113.         }
  1114.         // 获取响应码
  1115.         httpResult.code = _GetStatusCode(m_hRequest);
  1116.         if (httpResult.code < 200 || httpResult.code >= 300)
  1117.         {
  1118.             _PrintError(_T("_GetStatusCode"));
  1119.             break;
  1120.         }
  1121.         // 查询文件大小
  1122.         ULONGLONG ullContentLength = 0;
  1123.         if (!_QueryContentLength(&ullContentLength))
  1124.         {
  1125.             _PrintError(_T("QueryContentLength"));
  1126.             break;
  1127.         }
  1128.         // 是否支持接收范围请求
  1129.         bool fAcceptRange =  _IsSupportAcceptRanges();
  1130.         // 多线程接收请求数据
  1131.         if (ullContentLength > 0 && fAcceptRange && dwThreadCount > 1)
  1132.         {
  1133.             httpResult = _MultiThreadRequest(url, ullContentLength, dwThreadCount);
  1134.         }
  1135.         else
  1136.         {
  1137.             // 单线程接收请求数据
  1138.             if (!_InternetReadData(httpResult.result))
  1139.             {
  1140.                 _PrintError(_T("_InternetReadData"));
  1141.                 break;
  1142.             }
  1143.         }
  1144.     } while (false);
  1145.     // 关闭相关句柄
  1146.     Close();
  1147.     return httpResult;
  1148. }
  1149. CWinHttpResult CWinHttpClient::_MultiThreadRequest(const _tstring& url, ULONGLONG ullContentLength, DWORD dwThreadCount/* = 1*/)
  1150. {
  1151.     CWinHttpResult httpResult;
  1152.     std::atomic<int> nTaskCount = dwThreadCount;
  1153.     bool fQuit = false;
  1154.     DWORD ullSinglePackageSize = ullContentLength / dwThreadCount;
  1155.     std::vector<CWinHttpClient> vTaskClients;
  1156.     vTaskClients.resize(dwThreadCount);
  1157.     std::vector<CWinHttpResult> vTaskResults;
  1158.     vTaskResults.resize(dwThreadCount);
  1159.     std::vector<std::thread> vTaskThreads;
  1160.     for (DWORD i = 0; i < dwThreadCount; i++)
  1161.     {
  1162.         ULONGLONG ullBeginPos = ullSinglePackageSize * i;
  1163.         ULONGLONG ullPackageSize = (i != dwThreadCount - 1) ? ullSinglePackageSize : ullContentLength - ullBeginPos;
  1164.         vTaskClients[i].SetReQuestDataRange(ullBeginPos, ullPackageSize);
  1165.         vTaskClients[i].m_strAgent = m_strAgent;
  1166.         vTaskClients[i].m_strHeader = m_strHeader;
  1167.         vTaskClients[i].m_strParam = m_strParam;
  1168.         vTaskClients[i].m_strUrl = m_strUrl;
  1169.         vTaskClients[i].m_strFilePath = m_strFilePath;
  1170.         vTaskClients[i].m_fPrint = m_fPrint;
  1171.         vTaskThreads.emplace_back(std::thread([&vTaskClients, &vTaskResults, i, url, &nTaskCount, &fQuit, this]() {
  1172.             while (!fQuit)
  1173.             {
  1174.                 vTaskClients[i].m_AsyncData.AsyncResult.dwResult = 0;
  1175.                 vTaskClients[i].m_AsyncData.AsyncResult.dwError = 0;
  1176.                 // 修改请求起始位置
  1177.                 vTaskClients[i].m_ullStart += vTaskClients[i].m_ullDownload;
  1178.                 vTaskResults[i] = vTaskClients[i]._Get(url, [](const WINHTTP_PROGRESS_INFO& progress) {
  1179.                     return true;
  1180.                     }
  1181.                 );
  1182.                 if (vTaskResults[i].code >= 200 && vTaskResults[i].code < 300)
  1183.                 {
  1184.                     break;
  1185.                 }
  1186.             }
  1187.             nTaskCount--;
  1188.             }
  1189.         )
  1190.         );
  1191.     }
  1192.     // 进度统计用变量
  1193.     clock_t refreshInterval = 1000;
  1194.     clock_t curTime = ::clock();
  1195.     clock_t startTime = curTime;
  1196.     clock_t lastTime = curTime;
  1197.     double lfRemainTime = 0.0f;
  1198.     double lfSpeed = 0.0f;
  1199.     ULONGLONG ullLastDownload = 0;
  1200.     ULONGLONG ullTotalLength = ullContentLength;
  1201.     while (!fQuit)
  1202.     {
  1203.         ULONGLONG ullDownloaded = 0;
  1204.         // 进度计算
  1205.         if (m_cbProgress)
  1206.         {
  1207.             // 速度计算
  1208.             clock_t curTime = ::clock();
  1209.             if (curTime - lastTime >= refreshInterval  || 0 == nTaskCount)
  1210.             {
  1211.                 for (auto& item : vTaskClients)
  1212.                 {
  1213.                     ullDownloaded += item.m_ullDownload;
  1214.                 }
  1215.                 lfSpeed = (double)(ullDownloaded - ullLastDownload) / ((double)refreshInterval / 1000.0f);
  1216.                 if (isinf(lfSpeed) || isnan(lfSpeed))
  1217.                 {
  1218.                     lfSpeed = 0.0f;
  1219.                 }
  1220.                 // 进度报告
  1221.                 {
  1222.                     WINHTTP_PROGRESS_INFO progress = { 0 };
  1223.                     progress.lfProgress = (double)ullDownloaded / (double)ullTotalLength;
  1224.                     progress.ullCur = ullDownloaded;
  1225.                     progress.ullTotal = ullTotalLength;
  1226.                     progress.lfSpeed = lfSpeed;
  1227.                     progress.costTime = curTime - startTime;
  1228.                     progress.lfRemainTime = ((double)(ullTotalLength - ullDownloaded)) / lfSpeed;
  1229.                     progress.nActiveThread = nTaskCount;
  1230.                     progress.nTotalThread = dwThreadCount;
  1231.                     if (!m_cbProgress(progress))
  1232.                     {
  1233.                         fQuit = true;
  1234.                         break;
  1235.                     }
  1236.                 }
  1237.                 lastTime = curTime;
  1238.                 ullLastDownload = ullDownloaded;
  1239.             }
  1240.         }
  1241.         if (0 == nTaskCount)
  1242.         {
  1243.             break;
  1244.         }
  1245.         ::Sleep(10);
  1246.     }
  1247.     // 主动退出
  1248.     if (fQuit)
  1249.     {
  1250.         for (auto& item : vTaskClients)
  1251.         {
  1252.             item.Close();
  1253.         }
  1254.     }
  1255.     // 等待线程结束
  1256.     for (auto& item : vTaskThreads)
  1257.     {
  1258.         if (item.joinable())
  1259.         {
  1260.             item.join();
  1261.         }
  1262.     }
  1263.     // 结果拼接
  1264.     if(m_strFilePath.empty())
  1265.     {
  1266.         for (auto& item : vTaskResults)
  1267.         {
  1268.             httpResult.result += item.result;
  1269.             if (item.code >= 200 && item.code < 300)
  1270.             {
  1271.                 httpResult.code = item.code;
  1272.             }
  1273.             else
  1274.             {
  1275.                 httpResult.code = 0;
  1276.             }
  1277.         }
  1278.     }
  1279.     return httpResult;
  1280. }
  1281. std::string CWinHttpClient::_WStrToMultiStr(UINT CodePage, const std::wstring& str)
  1282. {
  1283.     int cbMultiByte = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, NULL, 0, NULL, 0);
  1284.     std::string strResult(cbMultiByte, 0);
  1285.     size_t nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size(), NULL, NULL);
  1286.     strResult.resize(nConverted);
  1287.     return strResult;
  1288. }
  1289. std::wstring CWinHttpClient::_MultiStrToWStr(UINT CodePage, const std::string& str)
  1290. {
  1291.     int cchWideChar = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, NULL);
  1292.     std::wstring strResult(cchWideChar, 0);
  1293.     size_t nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size());
  1294.     strResult.resize(nConverted);
  1295.     return strResult;
  1296. }
  1297. std::string CWinHttpClient::TStrToU8Str(const _tstring& str)
  1298. {
  1299. #ifdef _UNICODE
  1300.     return _WStrToMultiStr(CP_UTF8, str);
  1301. #else
  1302.     return _WStrToMultiStr(CP_UTF8, _MultiStrToWStr(CP_ACP, str));
  1303. #endif
  1304. }
  1305. std::wstring CWinHttpClient::TStrToWStr(const _tstring& str)
  1306. {
  1307. #ifdef _UNICODE
  1308.     return str;
  1309. #else
  1310.     return _MultiStrToWStr(CP_ACP, str);
  1311. #endif
  1312. }
  1313. _tstring CWinHttpClient::WStrToTStr(const std::wstring& str)
  1314. {
  1315. #ifdef _UNICODE
  1316.     return str;
  1317. #else
  1318.     return _WStrToMultiStr(CP_ACP, str);
  1319. #endif
  1320. }
  1321. void CWinHttpClient::SetAgent(const _tstring strAgent)
  1322. {
  1323.     m_strAgent = strAgent;
  1324. }
  1325. void CWinHttpClient::SetReQuestDataRange(ULONGLONG ullStart, ULONGLONG ullLength)
  1326. {
  1327.     m_ullStart = ullStart;
  1328.     m_ullLength = ullLength;
  1329. }
  1330. void CWinHttpClient::SetPrintStatus(bool fEnable/* = true*/)
  1331. {
  1332.     m_fPrint = fEnable;
  1333. }
  1334. bool CWinHttpClient::_QueryContentLength(PULONGLONG lpUllContentLength)
  1335. {
  1336.     DWORD dwInfoLevel = WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER64;
  1337.     ULONGLONG ullContentLength = 0;
  1338.     DWORD dwBufferLength = sizeof(ullContentLength);
  1339.     DWORD dwIndex = 0;
  1340.     if (!_WinHttpQueryHeaders(dwInfoLevel, WINHTTP_HEADER_NAME_BY_INDEX, &ullContentLength, &dwBufferLength, &dwIndex))
  1341.     {
  1342.         // 无法找到请求的标头
  1343.         if (ERROR_WINHTTP_HEADER_NOT_FOUND == ::GetLastError())
  1344.         {
  1345.             *lpUllContentLength = -1;
  1346.             return true;
  1347.         }
  1348.         _PrintError(_T("WinHttpQueryHeaders"));
  1349.         *lpUllContentLength = 0;
  1350.         return false;
  1351.     }
  1352.     *lpUllContentLength = ullContentLength;
  1353.     return true;
  1354. }
  1355. bool CWinHttpClient::_IsSupportAcceptRanges()
  1356. {
  1357.     WCHAR szBuf[MAX_PATH] = { 0 };
  1358.     DWORD dwBufferLength = sizeof(szBuf);
  1359.     DWORD dwIndex = 0;
  1360.     if (!_WinHttpQueryHeaders(WINHTTP_QUERY_ACCEPT_RANGES, WINHTTP_HEADER_NAME_BY_INDEX, &szBuf, &dwBufferLength, &dwIndex))
  1361.     {
  1362.         return false;
  1363.     }
  1364.     if (0 == _wcsicmp(szBuf, L"bytes"))
  1365.     {
  1366.         return true;
  1367.     }
  1368.     return false;
  1369. }
  1370. bool CWinHttpClient::_SetRequestDataRange(LONGLONG nBegin, LONGLONG nLength)
  1371. {
  1372.     WCHAR szBuf[MAX_PATH] = { 0 };
  1373.     if (0 == nLength)
  1374.     {
  1375.         (void)::StringCchPrintfW(szBuf, _countof(szBuf), L"Range:bytes=%lld-%lld", nBegin, nBegin);
  1376.     }
  1377.     else if (nLength > 0)
  1378.     {
  1379.         (void)::StringCchPrintfW(szBuf, _countof(szBuf), L"Range:bytes=%lld-%lld", nBegin, nBegin + nLength - 1);
  1380.     }
  1381.     else
  1382.     {
  1383.         (void)::StringCchPrintfW(szBuf, _countof(szBuf), L"Range:bytes=%lld-", nBegin);
  1384.     }
  1385.     return ::WinHttpAddRequestHeaders(m_hRequest, szBuf, (DWORD)wcslen(szBuf), 0);
  1386. }
  1387. DWORD CWinHttpClient::_GetStatusCode(HINTERNET hRequest)
  1388. {
  1389.     DWORD dwInfoLevel = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
  1390.     DWORD dwRespCode = 0;
  1391.     DWORD dwBufferLength = sizeof(dwRespCode);
  1392.     DWORD dwIndex = 0;
  1393.     if (!_WinHttpQueryHeaders(dwInfoLevel, WINHTTP_HEADER_NAME_BY_INDEX, &dwRespCode, &dwBufferLength, &dwIndex))
  1394.     {
  1395.         return dwRespCode;
  1396.     }
  1397.     return dwRespCode;
  1398. }
  1399. bool CWinHttpClient::_InternetReadData(std::string& strData)
  1400. {
  1401.     HANDLE hFile = INVALID_HANDLE_VALUE;
  1402.     bool fResult = false;
  1403.     // 查询文件大小
  1404.     ULONGLONG ullContentLength = 0;
  1405.     if (!_QueryContentLength(&ullContentLength))
  1406.     {
  1407.         _PrintError(_T("QueryContentLength"));
  1408.         return false;
  1409.     }
  1410.     // 分配数据缓冲
  1411.     try
  1412.     {
  1413.         if (-1 != ullContentLength)
  1414.         {
  1415.             strData.resize(ullContentLength, 0);
  1416.         }
  1417.     }
  1418.     catch (...)
  1419.     {
  1420.         _PrintError(_T("std::string resize"));
  1421.         return false;
  1422.     }
  1423.     // 接收数据
  1424.     LPBYTE lpBufPos = (LPBYTE)strData.data();
  1425.     std::string strBuf;
  1426.     ULONGLONG ullDownloaded = 0;
  1427.     ULONGLONG ullTotalLength = ullContentLength;
  1428.     ULONGLONG ullLastDownload = 0;
  1429.     // 进度统计用变量
  1430.     clock_t refreshInterval = 1000;
  1431.     clock_t curTime = ::clock();
  1432.     clock_t startTime = curTime;
  1433.     clock_t lastTime = curTime;
  1434.     double lfRemainTime = 0.0f;
  1435.     double lfSpeed = 0.0f;
  1436.     if (!m_strFilePath.empty())
  1437.     {
  1438.         LARGE_INTEGER liDistanceToMove = { 0 };
  1439.         // 共享读写 创建/打开 文件, 多线程读写
  1440.         hFile = ::CreateFile(m_strFilePath.c_str(),
  1441.             GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
  1442.             NULL,
  1443.             OPEN_ALWAYS,
  1444.             FILE_ATTRIBUTE_ARCHIVE,
  1445.             NULL);
  1446.         if (INVALID_HANDLE_VALUE == hFile)
  1447.         {
  1448.             _PrintError(_T("CreateFile"));
  1449.             return false;
  1450.         }
  1451.         // 设置数据写入位置
  1452.         liDistanceToMove.QuadPart = m_ullStart;
  1453.         ::SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN);
  1454.     }
  1455.     do
  1456.     {
  1457.         // 检查可用数据
  1458.         DWORD dwNumberOfBytesAvailable = 0;
  1459.         DWORD dwNumberOfBytesRead = 0;
  1460.         if (!_WinHttpQueryDataAvailable(&dwNumberOfBytesAvailable))
  1461.         {
  1462.             _PrintError(_T("_WinHttpQueryDataAvailable"));
  1463.             break;
  1464.         }
  1465.         if (dwNumberOfBytesAvailable > 0)
  1466.         {
  1467.             try
  1468.             {
  1469.                 strBuf.resize(dwNumberOfBytesAvailable);
  1470.             }
  1471.             catch (...)
  1472.             {
  1473.                 _PrintError(_T("std::vector resize"));
  1474.                 break;
  1475.             }
  1476.             if (!_WinHttpReadData(&strBuf[0], dwNumberOfBytesAvailable, &dwNumberOfBytesRead))
  1477.             {
  1478.                 _PrintError(_T("_WinHttpReadData"));
  1479.                 break;
  1480.             }
  1481.             // 写入到缓冲
  1482.             if (-1 == ullContentLength)
  1483.             {
  1484.                 strData += strBuf;
  1485.                 ullDownloaded += strBuf.size();
  1486.             }
  1487.             else
  1488.             {
  1489.                 memcpy(lpBufPos, &strBuf[0], dwNumberOfBytesRead);
  1490.                 lpBufPos += dwNumberOfBytesRead;
  1491.                 ullDownloaded += dwNumberOfBytesRead;
  1492.             }
  1493.             if (INVALID_HANDLE_VALUE != hFile)
  1494.             {
  1495.                 DWORD dwWritten = 0;
  1496.                 if (!::WriteFile(hFile, strBuf.data(), dwNumberOfBytesRead, &dwWritten, NULL))
  1497.                 {
  1498.                     _PrintError(_T("WriteFile"));
  1499.                     break;
  1500.                 }
  1501.             }
  1502.             m_ullDownload += dwNumberOfBytesRead;
  1503.         }
  1504.         // 进度计算
  1505.         if (m_cbProgress)
  1506.         {
  1507.             // 速度计算
  1508.             clock_t curTime = ::clock();
  1509.             if (curTime - lastTime >= refreshInterval  || 0 == dwNumberOfBytesAvailable)
  1510.             {
  1511.                 lfSpeed = (double)(ullDownloaded - ullLastDownload) / ((double)refreshInterval / 1000.0f);
  1512.                 if (isinf(lfSpeed) || isnan(lfSpeed))
  1513.                 {
  1514.                     lfSpeed = 0.0f;
  1515.                 }
  1516.                 if (0 == dwNumberOfBytesAvailable)
  1517.                 {
  1518.                     ullTotalLength = ullDownloaded;
  1519.                 }
  1520.                 // 进度报告
  1521.                 {
  1522.                     WINHTTP_PROGRESS_INFO progress = { 0 };
  1523.                     progress.lfProgress = (double)ullDownloaded / (double)ullTotalLength;
  1524.                     progress.ullCur = ullDownloaded;
  1525.                     progress.ullTotal = ullTotalLength;
  1526.                     progress.lfSpeed = lfSpeed;
  1527.                     progress.costTime = curTime - startTime;
  1528.                     progress.lfRemainTime = ((double)(ullTotalLength - ullDownloaded)) / lfSpeed;
  1529.                     progress.nActiveThread = 1;
  1530.                     progress.nTotalThread = 1;
  1531.                     if (!m_cbProgress(progress))
  1532.                     {
  1533.                         break;
  1534.                     }
  1535.                 }
  1536.                 lastTime = curTime;
  1537.                 ullLastDownload = ullDownloaded;
  1538.             }
  1539.         }
  1540.         // 检查读取是否结束
  1541.         if (!dwNumberOfBytesAvailable)
  1542.         {
  1543.             fResult = true;
  1544.             break;
  1545.         }
  1546.     } while (true);
  1547.     if (INVALID_HANDLE_VALUE != hFile)
  1548.     {
  1549.         ::CloseHandle(hFile);
  1550.     }
  1551.     return fResult;
  1552. }
  1553. void CWinHttpClient::_PrintError(LPCTSTR lpszError) const
  1554. {
  1555.     return;
  1556.     ConsoleOutput(_T("[Error][LastError: %d Error: %d Result: %d][%s]\r\n"),
  1557.         ::GetLastError(),
  1558.         m_AsyncData.AsyncResult.dwError,
  1559.         m_AsyncData.AsyncResult.dwResult,
  1560.         lpszError
  1561.     );
  1562. }
  1563. void CWinHttpClient::ConsoleOutput(LPCTSTR pFormat, ...)
  1564. {
  1565.     size_t nCchCount = MAX_PATH;
  1566.     _tstring strResult(nCchCount, 0);
  1567.     va_list args;
  1568.     va_start(args, pFormat);
  1569.     do
  1570.     {
  1571.         //格式化输出字符串
  1572.         int nSize = _vsntprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);
  1573.         if (-1 != nSize)
  1574.         {
  1575.             HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
  1576.             ::WriteConsole(console, strResult.c_str(), nSize, NULL, NULL);
  1577.             break;
  1578.         }
  1579.         //缓冲大小超限终止
  1580.         if (nCchCount >= INT32_MAX)
  1581.         {
  1582.             break;
  1583.         }
  1584.         //重新分配缓冲
  1585.         nCchCount *= 2;
  1586.         strResult.resize(nCchCount);
  1587.     } while (true);
  1588.     va_end(args);
  1589. }
  1590. #define WINHTTP_STATUS_TEXT(_code)  std::make_pair(_code, _T(#_code)),
  1591. void CWinHttpClient::_PrintStatus(DWORD dwCode, LPVOID lpvStatusInfo, DWORD dwStatusInfoLength)
  1592. {
  1593.     std::map<DWORD, _tstring> mapWinHttpCode = {
  1594.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_RESOLVING_NAME             )
  1595.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_NAME_RESOLVED              )
  1596.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER       )
  1597.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER        )
  1598.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SENDING_REQUEST            )
  1599.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_REQUEST_SENT               )
  1600.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE         )
  1601.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED          )
  1602.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION         )
  1603.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED          )
  1604.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_HANDLE_CREATED             )
  1605.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING             )
  1606.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_DETECTING_PROXY            )
  1607.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_REDIRECT                   )
  1608.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE      )
  1609.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SECURE_FAILURE             )
  1610.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE          )
  1611.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE             )
  1612.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_READ_COMPLETE              )
  1613.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE             )
  1614.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_REQUEST_ERROR              )
  1615.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE       )
  1616.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE    )
  1617.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE             )
  1618.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE          )
  1619.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_GETPROXYSETTINGS_COMPLETE  )
  1620.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SETTINGS_WRITE_COMPLETE    )
  1621.         WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SETTINGS_READ_COMPLETE     )
  1622.     };
  1623.     auto itFind = mapWinHttpCode.find(dwCode);
  1624.     if (mapWinHttpCode.end() != itFind)
  1625.     {
  1626.         ConsoleOutput(_T("%08X %-50s "), itFind->first, itFind->second.c_str());
  1627.     }
  1628.     if (dwCode == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER ||
  1629.         dwCode == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER ||
  1630.         dwCode == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED ||
  1631.         dwCode == WINHTTP_CALLBACK_STATUS_REDIRECT
  1632.         )
  1633.     {
  1634.         if (NULL != lpvStatusInfo)
  1635.         {
  1636.             LPWSTR lpData = (LPWSTR)lpvStatusInfo;
  1637.             ConsoleOutput(_T("Data %s"), WStrToTStr(lpData).c_str());
  1638.         }
  1639.     }
  1640.     if (dwCode == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE ||
  1641.         dwCode == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE ||
  1642.         dwCode == WINHTTP_CALLBACK_STATUS_REQUEST_SENT ||
  1643.         dwCode == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED ||
  1644.         dwCode == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE ||
  1645.         dwCode == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE
  1646.         )
  1647.     {
  1648.         if (NULL != lpvStatusInfo)
  1649.         {
  1650.             LPDWORD lpData = (LPDWORD)lpvStatusInfo;
  1651.             ConsoleOutput(_T("Data %d(%08X)"), *lpData, *lpData);
  1652.         }
  1653.     }
  1654.     if (dwCode == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
  1655.         )
  1656.     {
  1657.         if (NULL != lpvStatusInfo)
  1658.         {
  1659.             LPWINHTTP_ASYNC_RESULT lpData = (LPWINHTTP_ASYNC_RESULT)lpvStatusInfo;
  1660.             ConsoleOutput(_T("Error %d(%08X) Result: %d(%08X)"),
  1661.                 lpData->dwError, lpData->dwError,
  1662.                 lpData->dwResult, lpData->dwResult
  1663.             );
  1664.         }
  1665.     }
  1666.     ConsoleOutput(_T("\r\n"));
  1667. }
  1668. VOID CALLBACK CWinHttpClient::WinHttpStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwStatus, LPVOID lpvInfo, DWORD dwInfoLength)
  1669. {
  1670.     CWinHttpClient* pThis = (CWinHttpClient*)dwContext;
  1671.     if (nullptr != pThis)
  1672.     {
  1673.         pThis->_WinHttpStatusCallback(hInternet, dwStatus, lpvInfo, dwInfoLength);
  1674.     }
  1675. }
  1676. // 状态回调
  1677. void CWinHttpClient::_WinHttpStatusCallback(HINTERNET hInternet, DWORD dwStatus, LPVOID lpvInfo, DWORD dwInfoLength)
  1678. {
  1679.     if (m_fPrint)
  1680.     {
  1681.         _PrintStatus(dwStatus, lpvInfo, dwInfoLength);
  1682.     }
  1683.     switch (dwStatus)
  1684.     {
  1685.         // 关闭与服务器的连接。 lpvStatusInformation 参数为 NULL。
  1686.     case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
  1687.     {
  1688.         break;
  1689.     }
  1690.     // 已成功连接到服务器。 lpvStatusInformation 参数包含指向 LPWSTR 的指针,该指针以点表示法指示服务器的 IP 地址。
  1691.     case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
  1692.     {
  1693.         LPWSTR lpData = (LPWSTR)lpvInfo;
  1694.         break;
  1695.     }
  1696.     // 连接到服务器。 lpvStatusInformation 参数包含指向 LPWSTR 的指针,该指针以点表示法指示服务器的 IP 地址。
  1697.     case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
  1698.     {
  1699.         LPWSTR lpData = (LPWSTR)lpvInfo;
  1700.         break;
  1701.     }
  1702.     // 已成功关闭与服务器的连接。 lpvStatusInformation 参数为 NULL。
  1703.     case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
  1704.     {
  1705.         break;
  1706.     }
  1707.     // 可以使用 WinHttpReadData 检索数据。 lpvStatusInformation 参数指向包含可用数据字节数的 DWORD。
  1708.     // dwStatusInformationLength 参数本身是 4 (DWORD) 的大小。
  1709.     case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:// WinHttpQueryDataAvailable 完成
  1710.     {
  1711.         DWORD lpSize = *(LPDWORD)lpvInfo;
  1712.         m_AsyncData.dwSize = lpSize;
  1713.         m_AsyncData.AsyncResult.dwResult = 0;
  1714.         ::SetEvent(m_AsyncData.hEvent);
  1715.         break;
  1716.     }
  1717.     // 已创建 HINTERNET 句柄。 lpvStatusInformation 参数包含指向 HINTERNET 句柄的指针。
  1718.     case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
  1719.     {
  1720.         LPHINTERNET pHandle = (LPHINTERNET)lpvInfo;
  1721.         m_AsyncData.dwContext = (DWORD_PTR)*pHandle;
  1722.         ::SetEvent(m_AsyncData.hEvent);
  1723.         break;
  1724.     }
  1725.     // 此句柄值已终止。 lpvStatusInformation 参数包含指向 HINTERNET 句柄的指针。 此句柄不再有回调。
  1726.     case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
  1727.     {
  1728.         LPHINTERNET pHandle = (LPHINTERNET)lpvInfo;
  1729.         break;
  1730.     }
  1731.     // 响应标头已收到,可用于 WinHttpQueryHeaders。 lpvStatusInformation 参数为 NULL。
  1732.     case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: // WinHttpReceiveResponse 完成
  1733.     {
  1734.         m_AsyncData.AsyncResult.dwResult = 0;
  1735.         ::SetEvent(m_AsyncData.hEvent);
  1736.         break;
  1737.     }
  1738.     // 从服务器收到中间 (100 级别) 状态代码消息。 lpvStatusInformation 参数包含指向指示状态代码的 DWORD 的指针。
  1739.     case WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE:
  1740.     {
  1741.         LPDWORD pStatusCode = (LPDWORD)lpvInfo;
  1742.         break;
  1743.     }
  1744.     // 已成功找到服务器的 IP 地址。 lpvStatusInformation 参数包含指向 LPWSTR 的指针,该指针指示已解析的名称。
  1745.     case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
  1746.     {
  1747.         LPWSTR lpResolved = (LPWSTR)lpvInfo;
  1748.         break;
  1749.     }
  1750.     // 已成功从服务器读取数据。
  1751.     // lpvStatusInformation 参数包含指向调用 WinHttpReadData 中指定的缓冲区的指针。
  1752.     // dwStatusInformationLength 参数包含读取的字节数。
  1753.     // WinHttpWebSocketReceive 使用时,lpvStatusInformation 参数包含指向WINHTTP_WEB_SOCKET_STATUS结构的指针,
  1754.     // dwStatusInformationLength 参数指示 lpvStatusInformation 的大小。
  1755.     case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: // WinHttpReadData 完成
  1756.     {
  1757.         LPBYTE* ppBuf = (LPBYTE*)lpvInfo;
  1758.         DWORD dwRead = dwInfoLength;
  1759.         m_AsyncData.AsyncResult.dwResult = 0;
  1760.         m_AsyncData.dwSize = dwRead;
  1761.         ::SetEvent(m_AsyncData.hEvent);
  1762.         break;
  1763.     }
  1764.     // 等待服务器响应请求。 lpvStatusInformation 参数为 NULL。
  1765.     case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
  1766.     {
  1767.         break;
  1768.     }
  1769.     // HTTP 请求将自动重定向请求。 lpvStatusInformation 参数包含指向指示新 URL 的 LPWSTR 的指针。
  1770.     // 此时,应用程序可以使用重定向响应读取服务器返回的任何数据,并且可以查询响应标头。 它还可以通过关闭句柄来取消操作。
  1771.     case WINHTTP_CALLBACK_STATUS_REDIRECT:
  1772.     {
  1773.         LPWSTR lpData = (LPWSTR)lpvInfo;
  1774.         break;
  1775.     }
  1776.     // 发送 HTTP 请求时出错。 lpvStatusInformation 参数包含指向WINHTTP_ASYNC_RESULT结构的指针。
  1777.     // 其 dwResult 成员指示被调用函数的 ID,dwError 指示返回值。
  1778.     case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
  1779.     {
  1780.         DWORD dwError = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
  1781.         LPWINHTTP_ASYNC_RESULT pAsyncResult = (LPWINHTTP_ASYNC_RESULT)lpvInfo;
  1782.         m_AsyncData.AsyncResult = *pAsyncResult;
  1783.         ::SetEvent(m_AsyncData.hEvent);
  1784.         break;
  1785.     }
  1786.     // 已成功将信息请求发送到服务器。 lpvStatusInformation 参数包含指向 DWORD 的指针,该指针指示发送的字节数。
  1787.     case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
  1788.     {
  1789.         LPDWORD pSentBytes = (LPDWORD)lpvInfo;
  1790.         break;
  1791.     }
  1792.     // 查找服务器名称的 IP 地址。 lpvStatusInformation 参数包含指向要解析的服务器名称的指针。
  1793.     case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
  1794.     {
  1795.         LPWSTR lpName = (LPWSTR)lpvInfo;
  1796.         break;
  1797.     }
  1798.     // 已成功从服务器收到响应。 lpvStatusInformation 参数包含指向指示接收字节数的 DWORD 的指针。
  1799.     case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
  1800.     {
  1801.         LPDWORD pRecv = (LPDWORD)lpvInfo;
  1802.         break;
  1803.     }
  1804.     // 在与服务器建立安全 (HTTPS) 连接时遇到一个或多个错误。
  1805.     // lpvStatusInformation 参数包含指向 DWORD 的指针,该指针是错误值的按位 OR 组合。
  1806.     // 有关详细信息,请参阅 lpvStatusInformation 的说明。
  1807.     case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
  1808.     {
  1809.         DWORD dwCode = *(LPDWORD)lpvInfo;
  1810.         switch (dwCode)
  1811.         {
  1812.             // 证书吊销检查已启用,但吊销检查未能验证证书是否已吊销。 用于检查吊销的服务器可能无法访问
  1813.         case WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED:
  1814.         {
  1815.             break;
  1816.         }
  1817.         // SSL 证书无效
  1818.         case WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT:
  1819.         {
  1820.             break;
  1821.         }
  1822.         // SSL 证书已吊销
  1823.         case WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED:
  1824.         {
  1825.             break;
  1826.         }
  1827.         // 函数不熟悉生成服务器证书的证书颁发机构
  1828.         case WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA:
  1829.         {
  1830.             break;
  1831.         }
  1832.         // SSL 证书公用名 (主机名字段) 不正确,例如,如果输入 www.microsoft.com 且证书上的公用名显示 www.msn.com
  1833.         case WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID:
  1834.         {
  1835.             break;
  1836.         }
  1837.         // 从服务器收到的 SSL 证书日期不正确。 证书已过期。
  1838.         case WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID:
  1839.         {
  1840.             break;
  1841.         }
  1842.         // 应用程序在加载 SSL 库时遇到内部错误
  1843.         case WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR:
  1844.         {
  1845.             break;
  1846.         }
  1847.         }
  1848.         break;
  1849.     }
  1850.     // 将信息请求发送到服务器。 lpvStatusInformation 参数为 NULL。
  1851.     case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
  1852.     {
  1853.         break;
  1854.     }
  1855.     // 请求已成功完成。
  1856.     // lpvStatusInformation 参数是传递给 WinHttpSendRequest (初始请求正文) 的 lpOptional 值,
  1857.     // dwStatusInformationLength 参数指示 (传递到 WinHttpSendRequest) 传递到 winHttpSendRequest 的 dwOptionalLength 值成功写入此类初始正文字节的数目。
  1858.     case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:  // WinHttpSendRequest 完成
  1859.     {
  1860.         m_AsyncData.AsyncResult.dwResult = 0;
  1861.         ::SetEvent(m_AsyncData.hEvent);
  1862.         break;
  1863.     }
  1864.     // 数据已成功写入服务器。 lpvStatusInformation 参数包含指向 DWORD 的指针,该指针指示写入的字节数。
  1865.     // 当 WinHttpWebSocketSend 使用时, lpvStatusInformation 参数包含指向WINHTTP_WEB_SOCKET_STATUS结构的指针,
  1866.     // dwStatusInformationLength 参数指示 lpvStatusInformation 的大小。
  1867.     case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:    // WinHttpWriteData 完成
  1868.     {
  1869.         m_AsyncData.AsyncResult.dwResult = 0;
  1870.         ::SetEvent(m_AsyncData.hEvent);
  1871.         break;
  1872.     }
  1873.     // 通过调用 WinHttpGetProxyForUrlEx 启动的操作已完成。 可以使用 WinHttpReadData 检索数据。
  1874.     case WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE:
  1875.     {
  1876.         break;
  1877.     }
  1878.     // 通过调用 WinHttpWebSocketClose 成功关闭了连接。 lpvStatusInformation 参数为 NULL。
  1879.     case WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE:
  1880.     {
  1881.         break;
  1882.     }
  1883.     // 通过调用 WinHttpWebSocketShutdown 成功关闭连接。 lpvStatusInformation 参数为 NULL。
  1884.     case WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE:
  1885.     {
  1886.         break;
  1887.     }
  1888.     }
  1889. }
  1890. bool CWinHttpClient::_WinHttpOpen(LPCWSTR pszAgentW, DWORD dwAccessType, LPCWSTR pszProxyW, LPCWSTR pszProxyBypassW, DWORD dwFlags)
  1891. {
  1892.     // 初始化 WinHTTP 函数的使用并返回 WinHTTP 会话句柄。
  1893.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpopen
  1894.     m_hSession = ::WinHttpOpen(
  1895.         pszAgentW,                      // 指向字符串变量的指针,此名称用作 HTTP 协议中的 用户代理
  1896.         dwAccessType,                   // 所需的访问类型
  1897.         pszProxyW,                      // 代理访问时要使用的代理服务器的名称
  1898.         pszProxyBypassW,                // 代理访问时要使用的代理服务器的密码
  1899.         dwFlags                         // 指示影响此函数行为的各种选项的标志
  1900.     );
  1901.     return NULL != m_hSession;
  1902. }
  1903. bool CWinHttpClient::_WinHttpConnect(LPCWSTR pswzServerName, INTERNET_PORT nServerPort)
  1904. {
  1905.     // 指定 HTTP 请求的初始目标服务器,并将 HINTERNET 连接句柄返回到该初始目标的 HTTP 会话
  1906.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpconnect
  1907.     // 即使 WinHTTP 在异步模式下使用(即在 WinHttpOpen中设置 WINHTTP_FLAG_ASYNC),此函数也会同步运行
  1908.     m_hConnect = ::WinHttpConnect(
  1909.         m_hSession,                   // 由先前调用 WinHttpOpen 返回的有效 HINTERNETWinHTTP 会话句柄
  1910.         pswzServerName,             // HTTP 服务器的主机名
  1911.         nServerPort,                // 建立连接的服务器上的 TCP/IP 端口
  1912.         0                           // 保留参数, 必须为0
  1913.     );
  1914.     return NULL != m_hConnect;
  1915. }
  1916. bool CWinHttpClient::_WinHttpOpenRequest(const _tstring& strVerb, LPCWSTR pwszObjectName, LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR *ppwszAcceptTypes, DWORD dwFlags)
  1917. {
  1918.     // 创建 HTTP 请求句柄
  1919.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpopenrequest
  1920.     HINTERNET hRequest = ::WinHttpOpenRequest(
  1921.         m_hConnect,                         // WinHttpConnect 返回的 HTTP 会话的 HINTERNET 连接句柄
  1922.         TStrToWStr(strVerb).c_str(),        // 请求的 HTTP 谓词
  1923.         pwszObjectName,                     // 指定 HTTP 谓词的目标资源名称
  1924.         pwszVersion,                        // HTTP 版本的字符串的指针
  1925.         pwszReferrer,                       // 指定从中获取 请求 pwszObjectName 中的 URL 的文档的 URL
  1926.         ppwszAcceptTypes,                   // 指定客户端接受的媒体类型
  1927.         dwFlags                             // Internet 标志值
  1928.     );
  1929.     // 等待异步请求完成
  1930.     if (!_WaitForAsyncEvent())
  1931.     {
  1932.         return NULL;
  1933.     }
  1934.     m_hRequest = (HINTERNET)m_AsyncData.dwContext;
  1935.     return NULL != m_hRequest;
  1936. }
  1937. bool CWinHttpClient::_WinHttpSetSessionTimeouts(int nResolveTimeout, int nConnectTimeout,int nSendTimeout,int nReceiveTimeout)
  1938. {
  1939.     // 设置与 HTTP 事务相关的超时。
  1940.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsettimeouts
  1941.     // 即使在异步模式下使用 WinHTTP (即在 WinHttpOpen) 中设置了WINHTTP_FLAG_ASYNC时,此函数也会同步运行。
  1942.     return ::WinHttpSetTimeouts(
  1943.         m_hSession,             // WinHttpOpen 或 WinHttpOpenRequest 返回的 HINTERNET 句柄。
  1944.         nResolveTimeout,        // 名称解析的超时值(以毫秒为单位)
  1945.         nConnectTimeout,        // 服务器连接请求的超时值(以毫秒为单位)
  1946.         nSendTimeout,           // 发送请求的超时值(以毫秒为单位)
  1947.         nReceiveTimeout         // 接收对请求的响应超超时值(以毫秒为单位)
  1948.     );
  1949. }
  1950. bool CWinHttpClient::_WinHttpSetRequestTimeouts(int nResolveTimeout, int nConnectTimeout,int nSendTimeout,int nReceiveTimeout)
  1951. {
  1952.     // 设置与 HTTP 事务相关的超时。
  1953.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsettimeouts
  1954.     // 即使在异步模式下使用 WinHTTP (即在 WinHttpOpen) 中设置了WINHTTP_FLAG_ASYNC时,此函数也会同步运行。
  1955.     return ::WinHttpSetTimeouts(
  1956.         m_hRequest,             // WinHttpOpen 或 WinHttpOpenRequest 返回的 HINTERNET 句柄。
  1957.         nResolveTimeout,        // 名称解析的超时值(以毫秒为单位)
  1958.         nConnectTimeout,        // 服务器连接请求的超时值(以毫秒为单位)
  1959.         nSendTimeout,           // 发送请求的超时值(以毫秒为单位)
  1960.         nReceiveTimeout         // 接收对请求的响应超超时值(以毫秒为单位)
  1961.     );
  1962. }
  1963. bool CWinHttpClient::_WinHttpSetStatusCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags
  1964. )
  1965. {
  1966.     // 设置回调函数
  1967.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsetstatuscallback
  1968.     WINHTTP_STATUS_CALLBACK statusCallback = ::WinHttpSetStatusCallback(
  1969.         m_hSession,                 // 要为其设置回调的 HINTERNET 句柄
  1970.         lpfnInternetCallback,       // 指向进度时要调用的回调函数的指针
  1971.         dwNotificationFlags,        // 无符号长整数值,该值指定标志以指示哪些事件激活回调函数
  1972.         0
  1973.     );
  1974.     if (WINHTTP_INVALID_STATUS_CALLBACK == statusCallback)
  1975.     {
  1976.         return false;
  1977.     }
  1978.     return true;
  1979. }
  1980. bool CWinHttpClient::_WaitForAsyncEvent(DWORD dwMilliseconds/* = INFINITE*/)
  1981. {
  1982.     // 等待异步事件响应
  1983.     m_AsyncData.dwWait = ::WaitForSingleObject(m_AsyncData.hEvent, dwMilliseconds);
  1984.     if (m_fAbort)
  1985.     {
  1986.         return false;
  1987.     }
  1988.     if (WAIT_OBJECT_0 == m_AsyncData.dwWait && 0 == m_AsyncData.AsyncResult.dwResult)
  1989.     {
  1990.         return true;
  1991.     }
  1992.     return false;
  1993. }
  1994. bool CWinHttpClient::_WinHttpSetSessionOption(DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength)
  1995. {
  1996.     // 设置 Internet 选项。
  1997.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsetoption
  1998.     return ::WinHttpSetOption(m_hSession, dwOption, lpBuffer, dwBufferLength);
  1999. }
  2000. bool CWinHttpClient::_WinHttpSetRequestOption(DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength)
  2001. {
  2002.     // 设置 Internet 选项。
  2003.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsetoption
  2004.     return ::WinHttpSetOption(m_hRequest, dwOption, lpBuffer, dwBufferLength);
  2005. }
  2006. bool CWinHttpClient::_WinHttpSendRequest(_tstring strHeader, LPVOID lpData, DWORD dwSize, DWORD_PTR dwContext)
  2007. {
  2008.     std::wstring wstrHeader = TStrToWStr(strHeader);
  2009.     LPCWSTR lpHeader = (LPCWSTR)wstrHeader.data();
  2010.     DWORD dwHeaderSize = (DWORD)wstrHeader.size();
  2011.     if (wstrHeader.empty())
  2012.     {
  2013.         lpHeader = WINHTTP_NO_ADDITIONAL_HEADERS;
  2014.         dwHeaderSize = 0;
  2015.     }
  2016.     bool fResult = false;
  2017.     do
  2018.     {
  2019.         // 发送请求
  2020.         // 将指定的请求发送到 HTTP 服务器。
  2021.         // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsendrequest
  2022.         if (::WinHttpSendRequest(
  2023.             m_hRequest,             //WinHttpOpenRequest 返回的 HINTERNET 句柄
  2024.             lpHeader,               //要追加到请求的其他标头
  2025.             dwHeaderSize,           //附加标头的长度(以字符为单位)
  2026.             lpData,                 //请求标头之后发送的任何可选数据
  2027.             dwSize,                 //可选数据的长度(以字节为单位)
  2028.             dwSize,                 //发送的总数据的长度
  2029.             dwContext               //上下文
  2030.         ))
  2031.         {
  2032.             fResult = true;
  2033.             break;
  2034.         }
  2035.         // 安全 HTTP 服务器需要客户端证书
  2036.         if (ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED != ::GetLastError())
  2037.         {
  2038.             break;
  2039.         }
  2040.         // 设置 Internet 选项
  2041.         // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsetoption
  2042.         if (!_WinHttpSetRequestOption(
  2043.             WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
  2044.             WINHTTP_NO_CLIENT_CERT_CONTEXT,
  2045.             0
  2046.         ))
  2047.         {
  2048.             break;
  2049.         }
  2050.         // 再次发送请求
  2051.         // 将指定的请求发送到 HTTP 服务器。
  2052.         // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsendrequest
  2053.         if (!::WinHttpSendRequest(
  2054.             m_hRequest,             // WinHttpOpenRequest 返回的 HINTERNET 句柄
  2055.             lpHeader,               // 要追加到请求的其他标头
  2056.             dwHeaderSize,           // 附加标头的长度(以字符为单位)
  2057.             lpData,                 // 请求标头之后发送的任何可选数据
  2058.             dwSize,                 // 可选数据的长度(以字节为单位)
  2059.             dwSize,                 // 发送的总数据的长度
  2060.             NULL
  2061.         ))
  2062.         {
  2063.             break;
  2064.         }
  2065.         fResult = true;
  2066.     } while (false);
  2067.     if (!fResult)
  2068.     {
  2069.         return false;
  2070.     }
  2071.     // 等待异步请求完成
  2072.     return _WaitForAsyncEvent();
  2073. }
  2074. bool CWinHttpClient::_WinHttpReceiveResponse(HINTERNET hRequest)
  2075. {
  2076.     // 等待接收 WinHttpSendRequest 发起的 HTTP 请求的响应。
  2077.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpreceiveresponse
  2078.     if (!::WinHttpReceiveResponse(
  2079.         hRequest,       // WINHttpOpenRequest 返回并由 WinHttpSendRequest 发送的 HINTERNET 句柄。
  2080.         NULL            // 此参数是保留的,必须为 NULL。
  2081.     ))
  2082.     {
  2083.         return false;
  2084.     }
  2085.     // 等待异步请求完成
  2086.     return _WaitForAsyncEvent();
  2087. }
  2088. bool CWinHttpClient::_WinHttpQueryHeaders(DWORD dwInfoLevel, LPCWSTR pwszName, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
  2089. {
  2090.     return ::WinHttpQueryHeaders(
  2091.         m_hRequest,                     //WinHttpOpenRequest 返回的 HINTERNET 请求句柄
  2092.         dwInfoLevel,                    //指定“查询信息标志”页上列出的属性标志和修饰符标志的组合
  2093.         pwszName,                       //标头名称
  2094.         lpBuffer,                       //接收信息的缓冲区
  2095.         lpdwBufferLength,               //数据缓冲区的长度
  2096.         lpdwIndex                       //从零开始的标头索引的指针,用于枚举具有相同名称的多个标头
  2097.     );
  2098. }
  2099. bool CWinHttpClient::_WinHttpQueryDataAvailable(LPDWORD lpdwNumberOfBytesAvailable)
  2100. {
  2101.     m_AsyncData.dwSize = 0;
  2102.     // 返回可使用 WinHttpReadData 读取的数据量(以字节为单位)。
  2103.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpquerydataavailable
  2104.     if (!::WinHttpQueryDataAvailable(
  2105.         m_hRequest,                     // WinHttpOpenRequest 返回的有效 HINTERNET 句柄。
  2106.                                         // WinHttpReceiveResponse 必须已为此句柄调用,并在调用 WinHttpQueryDataAvailable 之前完成。
  2107.         NULL                            // 指向接收可用字节数的无符号长整数变量的指针。
  2108.                                         // 在异步模式下使用 WinHTTP 时,始终将此参数设置为 NULL ,并在回调函数中检索数据;
  2109.                                         // 不这样做可能会导致内存故障。
  2110.     ))
  2111.     {
  2112.         return false;
  2113.     }
  2114.     // 等待异步请求完成
  2115.     if (!_WaitForAsyncEvent())
  2116.     {
  2117.         return false;
  2118.     }
  2119.     *lpdwNumberOfBytesAvailable = m_AsyncData.dwSize;
  2120.     return true;
  2121. }
  2122. bool CWinHttpClient::_WinHttpReadData(LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead)
  2123. {
  2124.     m_AsyncData.dwSize = 0;
  2125.     // 从 WinHttpOpenRequest 函数打开的句柄读取数据。
  2126.     // https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpreaddata
  2127.     if (!::WinHttpReadData(
  2128.         m_hRequest,             // 从上一次调用 WinHttpOpenRequest 返回的有效 HINTERNET 句柄。
  2129.         lpBuffer,               // 指向接收读取数据的缓冲区的指针。 确保此缓冲区在 WinHttpReadData 完成之前保持有效。
  2130.         dwNumberOfBytesToRead,  // 要读取的字节数的无符号长整数值。
  2131.         NULL                    // 指向接收读取字节数的无符号长整数变量的指针。
  2132.                                 // WinHttpReadData 在执行任何工作或错误检查之前将此值设置为零。
  2133.                                 // 异步使用 WinHTTP 时,始终将此参数设置为 NULL ,并在回调函数中检索信息;
  2134.                                 // 不这样做可能会导致内存故障。
  2135.     ))
  2136.     {
  2137.         return false;
  2138.     }
  2139.     // 等待异步请求完成
  2140.     if (!_WaitForAsyncEvent())
  2141.     {
  2142.         return false;
  2143.     }
  2144.     *lpdwNumberOfBytesRead = m_AsyncData.dwSize;
  2145.     return true;
  2146. }
复制代码
main.cpp
  1. #include <iostream>
  2. #include "CWinHttpClient.h"
  3. #define UPDATE_URL1          _T("https://gitee.com/flame_cyclone/fc_font_tool/raw/master/Release/update.json")
  4. #define UPDATE_URL2          _T("https://gitee.com/flame_cyclone/fc_font_tool/releases/download/1.0.0.4/FC_Font_Tool.exe")
  5. #define TEST_NVIDIA_DRIVER   _T("https://cn.download.nvidia.com/Windows/561.09/561.09-desktop-win10-win11-64bit-international-dch-whql.exe")
  6. #define TEST_AMD_DRIVER      _T("https://drivers.amd.com/drivers/whql-amd-software-adrenalin-edition-24.8.1-win10-win11-aug-rdna.exe")
  7. #define TEST_BILIBILI_DM     _T("https://api.live.bilibili.com/xlive/web-room/v1/dM/gethistory?roomid=4412054")
  8. #define TEST_WEPE_URL        _T("https://mirrors.lzu.edu.cn/wepe/WePE_64_V2.3.exe")
  9. int main()
  10. {
  11.     CWinHttpClient obj;
  12.     CWinHttpValue header = CWinHttpObject{
  13.         {_T("User-Agent"), _T("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0")},
  14.         {_T("Content-Type"), _T("application/json;charset=UTF-8")},
  15.         {_T("Accept"), _T("*/*")},
  16.         {_T("Referer"), _T("https://www.amd.com/")},
  17.     };
  18.     CWinHttpValue param = CWinHttpObject{
  19.         {_T("Accept"), _T("application/x-clarity-gzip")},
  20.         {_T("Accept-Encoding"), CWinHttpArray {_T("gzip"), _T("deflate"), _T("br"), _T("zstd")}},
  21.         {_T("Accept-Language"), CWinHttpArray {_T("zh-CN"), _T("zh;q=0.9"), _T("en;q=0.8"), _T("en-GB;q=0.7"), _T("en-US;q=0.6")}},
  22.     };
  23.     _tstring strGet = param.AsGetString();
  24.     _tstring strJsonParam = param.AsJsonString();
  25.     _tstring strHeader = param.AsHeaderString();
  26.     CWinHttpResult get = obj.Get(_T(R"(https://api.live.bilibili.com/xlive/web-room/v1/dM/gethistory?roomid=4412054)"), {}, header, nullptr);
  27.     get.result = obj.DecoderFromUtf8(get.result);
  28.     std::string strResult = CWinHttpClient::DecoderFromUtf8(R"({"string":"\uD83C\uDF0D\u6211\u662F\u5730\u7403"})");
  29.     bool fResult;
  30.     clock_t startTime = ::clock();
  31.     CWinHttpResult downResult = obj.DownloadFile(TEST_AMD_DRIVER, _T(""), header.AsHeaderString(), [](const WINHTTP_PROGRESS_INFO& progress) {
  32.         CWinHttpClient::ConsoleOutput(_T("%d/%d Time: %.3lfs Progress: %.3lf%% %.1lfMB/%.1lfMB Speed: %.1lf Mbps %.1lfMB/s RemainTime: %.1lfs\n"),
  33.         progress.nActiveThread, progress.nTotalThread,
  34.         (double)progress.costTime / 1000.0f,
  35.         progress.lfProgress * 100,
  36.         (double)progress.ullCur / (1024.0f * 1024.0f), (double)progress.ullTotal / (1024.0f * 1024.0f),
  37.         progress.lfSpeed / (1024.0f * 1024.0f) * 8.0f,
  38.         progress.lfSpeed / (1024.0f * 1024.0f),
  39.         progress.lfRemainTime
  40.         );
  41.     return true;
  42.         }, 2
  43.     );
  44.     clock_t endTime = ::clock();
  45.     CWinHttpClient::ConsoleOutput(_T("Cost Time: %dms\r\n"), endTime - startTime);
  46.     return 0;
  47. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表