Visual Studio发行模式阻止在调试模式下执行的代码执行。使用WinHTTP和多线程

问题描述 投票:0回答:1

我正在使用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,然后在循环中[,我使用了很多工具对其进行调试并查看其工作方式,但是我唯一有效的结论是,它处于永无止境的循环中,因为那条语句消耗了所有资源,从而阻止了进一步的进展(尽管该循环中的CPU使用率not 100%,在Ryzen上仅为9% 3600x)

Edit

:要澄清此is在没有thread_sleep的调试模式下工作的情况,仅在Relase模式下,它正在这样做。我正在使用WinHTTP发出GET请求,并且正在通过回调函数异步使用WinHttpOpen。 HINTERNET hSession = WinHttpOpen(L“”,WINHTTP_ACCESS_TYPE_NO_PROXY,WINHTTP_NO_PROXY_NAME,...
c++ multithreading window winhttp
1个回答
0
投票
您在g_nBitFlags上存在数据争用,因此行为未定义。一个线程正在g_nBitFlags上旋转,等待另一个线程对其进行更新,但是您没有使用原子变量或其他同步机制。因此,编译器有权假定变量不能更改。因此,在发布模式下,编译器仅决定优化重载g_nBitFlags,并且您的代码将继续在相同(恒定)值上循环。
© www.soinside.com 2019 - 2024. All rights reserved.