我正在使用WinHTTP发出GET请求,并且我正在与WinHttpOpen异步使用callback function。
HINTERNET hSession = WinHttpOpen(L"", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
if (hSession) {
void* phSession_Callback = WinHttpSetStatusCallback(hSession, (WINHTTP_STATUS_CALLBACK)hSession_Callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
if (phSession_Callback == WINHTTP_INVALID_STATUS_CALLBACK) {
//error handling code
}
}
在我的GET函数的末尾,我使用位域来存储数据。在该数据中,有一个标志表示请求成功完成的时间(即if check)。
while (1) {
std::this_thread::sleep_for(std::chrono::microseconds(1));
if ((g_nBitFlags >> 3) & 1) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnection);
WinHttpCloseHandle(hSession);
if ((g_nBitFlags >> 2) & 1) {
return -1;
} else if ((g_nBitFlags >> 1) & 1) {
return 0;
} else if ((g_nBitFlags >> 0) & 1) {
return g_nBitFlags >> 4;
}
}
}
这是我的hSession回调,它会触发该函数(作为单独的线程),该函数将设置是否成功接收到标志的标志。 (请注意,该线程不会等待加入,即使http请求在该worker函数之前完成,它也完全独立)
void WINAPI hSession_Callback(
IN HINTERNET hInternet,
IN DWORD_PTR dwContext,
IN DWORD dwInternetStatus,
IN LPVOID lpvStatusInformation,
IN DWORD dwStatusInformationLength
) {
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: {
std::thread(dataAvail_worker, &httplib::g_nBitFlags, lpvStatusInformation, hInternet).detach();
}
}
请求的编辑: dataAvail_worker:
#include "pch.h"
#include "dataAvail.h"
namespace httplib {
extern char* g_szDataBuffer;
}
void dataAvail_worker(unsigned short* pnBitfield, LPVOID lpvDataLength, HINTERNET hRequest) {
if (*(DWORD*)lpvDataLength > 0) {
if (httplib::g_szDataBuffer == nullptr) {
httplib::g_szDataBuffer = new char[*(DWORD*)lpvDataLength + 1];
httplib::g_szDataBuffer[*(DWORD*)lpvDataLength] = 0;
WinHttpReadData(hRequest, httplib::g_szDataBuffer, *(DWORD*)lpvDataLength, NULL);
} else {
char* temp_buffer = new char[strlen(httplib::g_szDataBuffer) + 1];
temp_buffer[strlen(httplib::g_szDataBuffer)] = 0;
memcpy(temp_buffer, httplib::g_szDataBuffer, strlen(httplib::g_szDataBuffer)); //-V575 (PVS-Studio FalseAlarm)
delete[] httplib::g_szDataBuffer;
httplib::g_szDataBuffer = new char[strlen(temp_buffer) + *(DWORD*)lpvDataLength + 1];
httplib::g_szDataBuffer[strlen(temp_buffer) + *(DWORD*)lpvDataLength] = 0;
memcpy(httplib::g_szDataBuffer, temp_buffer, strlen(temp_buffer));
WinHttpReadData(hRequest, httplib::g_szDataBuffer + strlen(temp_buffer), *(DWORD*)lpvDataLength, NULL);
delete[] temp_buffer;
}
} else *pnBitfield += (~(*pnBitfield << 12) & 0b1000);
}
请求的编辑: get.cpp:
#include "pch.h"
#include "get.hpp"
#include "callbacks/get_Session/Session.h"
void _appendToHeader(std::string& private_member, const char* szValue, const char* szHeaderName);
namespace httplib {
char* g_szDataBuffer = nullptr;
GetRequest::GetRequest(FILE* temp_pSTDOUT) {
*stdout = *temp_pSTDOUT;
m_szData = &g_szDataBuffer;
}
GetRequest::~GetRequest() {
delete[] *m_szData;
}
// XXXXXXXXXXXX0000 = X (12bit): Reserved for HTTP RETURN STATUS CODE
// XXXXXXXXXXXX0001 = request succsessfully finished + valid http return status code available
// XXXXXXXXXXXX0010 = return SendRequest() function with 0 (Ivalid URI)
// XXXXXXXXXXXX0100 = return SendRequest() function with -1 (SSL Certificate error)
// XXXXXXXXXXXX1000 = is the request finished / should the function return
USHORT g_nBitFlags = 0b0000'0000'0000'0000;
RESPONSE GetRequest::SendRequest(Address szURI, Port nPort) {
char* pStartEndpoint = (char*)strchr(szURI, L'/');
HINTERNET hSession = WinHttpOpen(L"", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
if (hSession) {
void* phSession_Callback = WinHttpSetStatusCallback(hSession, (WINHTTP_STATUS_CALLBACK)hSession_Callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
if (phSession_Callback == WINHTTP_INVALID_STATUS_CALLBACK) {
// TODO: Display the error message in the gui/console of DiscordPP
printf("TEMPORARY: GetLastError() from GetRequest::SendRequest at WinHttpOpen() async at WinHttpSetStatusCallback () (Cannot install callback function): %u\n", GetLastError()); // This is temporary. Should not be used.
}
} else {
// TODO: Display the error message in the gui/console of DiscordPP
printf("TEMPORARY: GetLastError() from GetRequest::SendRequest at WinHttpOpen() async: %u\n", GetLastError()); // This is temporary. Should not be used.
}
wchar_t* szwConnectionUri;
if (pStartEndpoint != nullptr) {
szwConnectionUri = new wchar_t[pStartEndpoint - szURI + 1];
ZeroMemory(szwConnectionUri, (pStartEndpoint - szURI + 1) * sizeof(wchar_t));
mbstowcs(szwConnectionUri, szURI, pStartEndpoint - szURI);
} else {
szwConnectionUri = new wchar_t[strlen(szURI) + 1];
ZeroMemory(szwConnectionUri, (strlen(szURI) + 1) * sizeof(wchar_t));
mbstowcs(szwConnectionUri, szURI, strlen(szURI));
}
HINTERNET hConnection = WinHttpConnect(hSession, szwConnectionUri, nPort, 0);
if (!hConnection) {
// TODO: Display the error message in the gui/console of DiscordPP
printf("TEMPORARY: GetLastError() from GetRequest::SendRequest at WinHttpConnect() async: %u\n", GetLastError()); // This is temporary. Should not be used.
if (GetLastError() == 12005)
return 0;
}
delete[] szwConnectionUri;
wchar_t* szwEndpointUri;
if (pStartEndpoint != nullptr) {
szwEndpointUri = new wchar_t[(strlen(szURI) - (pStartEndpoint - szURI)) + 1];
ZeroMemory(szwEndpointUri, ((strlen(szURI) - (pStartEndpoint - szURI)) + 1) * sizeof(wchar_t));
mbstowcs(szwEndpointUri, szURI + (pStartEndpoint - szURI), (strlen(szURI) - (pStartEndpoint - szURI)));
} else {
szwEndpointUri = (wchar_t*)L"/";
}
const wchar_t* szDataAcceptTypes[] = {L"", 0};
HINTERNET hRequest = WinHttpOpenRequest(hConnection, L"GET", szwEndpointUri, L"HTTP/2.0", WINHTTP_NO_REFERER, szDataAcceptTypes, WINHTTP_FLAG_SECURE);
if (!hRequest) {
// TODO: Display the error message in the gui/console of DiscordPP
printf("TEMPORARY: GetLastError() from GetRequest::SendRequest at WinHttpOpenRequest() async: %u\n", GetLastError ()); // This is temporary. Should not be used.
}
if (pStartEndpoint != nullptr) delete[] szwEndpointUri;
wchar_t* szwHeaders = strcmp(m_szHeadersOut.c_str(), "") ? new wchar_t[m_szHeadersOut.length() + 1] : nullptr;
if (szwHeaders != nullptr) {
ZeroMemory(szwHeaders, (m_szHeadersOut.length() + 1) * sizeof(wchar_t));
mbstowcs(szwHeaders, m_szHeadersOut.c_str(), m_szHeadersOut.length());
}
if (strcmp(m_szHeadersOut.c_str(), "") != 0 && szwHeaders != nullptr) {
if (!WinHttpAddRequestHeaders(hRequest, szwHeaders, wcslen(szwHeaders), WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE))
// TODO: Display the error message in the gui/console of DiscordPP
printf("TEMPORARY: GetLastError() from GetRequest::SendRequest at WinHttpOpenRequest() async: %u\n", GetLastError ()); // This is temporary. Should not be used.
}
if (szwHeaders != nullptr) {
if (!WinHttpSendRequest(hRequest, szwHeaders, wcslen(szwHeaders), WINHTTP_NO_REQUEST_DATA, NULL, 0, 0))
// TODO: Display the error message in the gui/console of DiscordPP
printf("TEMPORARY: GetLastError() from GetRequest::SendRequest at WinHttpOpenRequest() async: %u\n", GetLastError ()); // This is temporary. Should not be used.
} else {
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, NULL, 0, 0))
// TODO: Display the error message in the gui/console of DiscordPP
printf("TEMPORARY: GetLastError() from GetRequest::SendRequest at WinHttpOpenRequest() async: %u\n", GetLastError ()); // This is temporary. Should not be used.
}
DWORD dwOption = WINHTTP_DISABLE_REDIRECTS;
WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &dwOption, sizeof(dwOption));
while (1) {
std::this_thread::sleep_for(std::chrono::microseconds(1));
if ((g_nBitFlags >> 3) & 1) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnection);
WinHttpCloseHandle(hSession);
if ((g_nBitFlags >> 2) & 1) {
return -1;
} else if ((g_nBitFlags >> 1) & 1) {
return 0;
} else if ((g_nBitFlags >> 0) & 1) {
return g_nBitFlags >> 4;
}
}
}
}
bool GetRequest::AddHeader(HeaderType Header, HeaderValue szValue) {
switch (Header) {
case HeaderType::Accept:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Accept");
break;
case HeaderType::Accept_Encoding:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Accept-Encoding");
break;
case HeaderType::Authorization:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Authorization");
break;
case HeaderType::Connection:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Connection");
break;
case HeaderType::Content_Encoding:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Content-Encoding");
break;
case HeaderType::Content_Length:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Content-Length");
break;
case HeaderType::Content_Type:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Content-Type");
break;
case HeaderType::User_Agent:
_appendToHeader(m_szHeadersOut, (char*)szValue, "User-Agent");
break;
case HeaderType::Upgrade:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Upgrade");
break;
case HeaderType::Referer:
_appendToHeader(m_szHeadersOut, (char*)szValue, "Referer");
break;
default:
return false;
}
return true;
}
const char* GetRequest::GetData() {
return *m_szData;
}
}
void _appendToHeader(std::string &private_member, const char* szValue, const char* szHeaderName) {
private_member += szHeaderName;
private_member += ":";
private_member += szValue;
private_member += "\r\n";
}
请求的编辑: callback.cpp:
#include "pch.h"
#include "Session.h"
#include "workers/dataAvail/dataAvail.h"
#include "workers/headersAvail/headersAvail.h"
namespace httplib {
extern USHORT g_nBitFlags;
extern char* g_szDataBuffer;
}
void WINAPI hSession_Callback(
IN HINTERNET hInternet,
IN DWORD_PTR dwContext,
IN DWORD dwInternetStatus,
IN LPVOID lpvStatusInformation,
IN DWORD dwStatusInformationLength
) {
switch (dwInternetStatus) {
case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
WinHttpReceiveResponse(hInternet, 0);
break;
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: {
std::thread(headersAvail_worker, &httplib::g_nBitFlags, hInternet).detach();
WinHttpQueryDataAvailable(hInternet, NULL);
}
break;
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: {
std::thread(dataAvail_worker, &httplib::g_nBitFlags, lpvStatusInformation, hInternet).detach();
}
break;
case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
httplib::g_nBitFlags = 0;
httplib::g_nBitFlags += (~(httplib::g_nBitFlags << 13) & 0b0100);
httplib::g_nBitFlags += (~(httplib::g_nBitFlags << 12) & 0b1000);
break;
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
httplib::g_nBitFlags = 0;
httplib::g_nBitFlags += (~(httplib::g_nBitFlags << 14) & 0b0010);
httplib::g_nBitFlags += (~(httplib::g_nBitFlags << 12) & 0b1000);
break;
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
WinHttpQueryDataAvailable(hInternet, NULL);
break;
case WINHTTP_CALLBACK_STATUS_REDIRECT:
printf("Session handler: Redirection was attempted, and canceled. Redirect Destination: %s\n", (char*) lpvStatusInformation);
break;
}
}
[要求的编辑: main.cpp:
#include "pch.h"
#include "../../httplib/src/get.hpp"
#include "../../httplib/src/post.hpp"
#ifdef _DEBUG
#pragma comment(lib, "../httplib/bin/Debug_x86/httplib_Debug_x86.lib")
#else
#pragma comment(lib, "../httplib/bin/Release_x86/httplib_Release_x86.lib")
#endif
int main() {
//ToDo: Switch to WINDOWS sub, make gui with console to the side.
httplib::GetRequest get(stdout);
get.AddHeader(HeaderType::Authorization, "token");
int nGet = get.SendRequest("discord.com/api/channels/678958544606724115/messages?limit=1");
printf("HTTP/GET Method: %d\n\nData: %s\n\n\n\n", nGet, get.GetData());
}
所以我的问题是,为什么如果缺少GET函数末尾的thread sleep,然后在循环中[
Edit
:要澄清此is在没有thread_sleep的调试模式下工作的情况,仅在Relase模式下,它正在这样做。我正在使用WinHTTP发出GET请求,并且正在通过回调函数异步使用WinHttpOpen。 HINTERNET hSession = WinHttpOpen(L“”,WINHTTP_ACCESS_TYPE_NO_PROXY,WINHTTP_NO_PROXY_NAME,...g_nBitFlags
上存在数据争用,因此行为未定义。一个线程正在g_nBitFlags
上旋转,等待另一个线程对其进行更新,但是您没有使用原子变量或其他同步机制。因此,编译器有权假定变量不能更改。因此,在发布模式下,编译器仅决定优化重载g_nBitFlags
,并且您的代码将继续在相同(恒定)值上循环。