【问题标题】:Access violation in winhttp.dllwinhttp.dll 中的访问冲突
【发布时间】:2013-03-22 01:56:56
【问题描述】:

我正在尝试在 C++ 中使用 WinHTTP 执行 HTTP GET,但在解析名称后(在状态回调函数中获取 WINHTTP_CALLBACK_STATUS_NAME_RESOLVED 之后)它在某个时候崩溃。我在 winhttp.dll 中遇到访问冲突。调用栈是:

winhttp.dll!HTTP_USER_REQUEST::_IndicateSocketAddress() + 0x221ed 字节
winhttp.dll!HTTP_USER_REQUEST::OnDnsNameResolved() + 0x24 字节
winhttp.dll!WEBIO_REQUEST::_OnInformation() + 0x1c0c 字节 webio.dll!_WaIndicateHttpRequestInformation@16() + 0x15a 字节 webio.dll!_WaHttpInformationConnection@16() + 0x7a 字节
webio.dll!_WapTcpDnsQueryCompletionRoutine@12() + 0x2f 字节
webio.dll!_WapCallDnsQueryCompletion@12() + 0x6d 字节 webio.dll!_WaDnsResolutionWorker@8() + 0x157 字节 ntdll.dll!_TppSimplepExecuteCallback@8() + 0x7b 字节
ntdll.dll!TppWorkerThread@4() + 0x5a4 字节
kernel32.dll!@BaseThreadInitThunk@12() + 0x12 字节
ntdll.dll!
__RtlUserThreadStart@8() + 0x27 字节
ntdll.dll!__RtlUserThreadStart@8() + 0x1b 字节

相关代码为:

enum OnlineState
{
    OnlineState_Idle,
    OnlineState_Registering
};

static OnlineState g_OnlineState = OnlineState_Idle;
HINTERNET g_Request = 0;
HINTERNET g_Connection = 0;
unsigned char g_HTTPBuffer[1024];

void HTTPRequestComplete()
{
    if(g_Request != 0)
    {
        WinHttpCloseHandle(g_Request);
        g_Request = 0;
    }
    if(g_Connection != 0)
    {
        WinHttpCloseHandle(g_Connection);
        g_Connection = 0;
    }
    g_OnlineState = OnlineState_Idle;
}

void HTTPAsyncCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
    switch(dwInternetStatus)
    {
        case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
        {
            // Get the response
            if (!WinHttpReceiveResponse(g_Request, 0))
            {
            // Failed to get the response
                HTTPRequestComplete();
                return;
            }
        }
        break;

        case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
        {
            DWORD statusCode = 0;
            DWORD statusCodeSize = sizeof(DWORD);

            if (!WinHttpQueryHeaders(g_Request,
                           WINHTTP_QUERY_STATUS_CODE |     WINHTTP_QUERY_FLAG_NUMBER,
                           WINHTTP_HEADER_NAME_BY_INDEX,
                           &statusCode,
                           &statusCodeSize,
                           WINHTTP_NO_HEADER_INDEX))
            {
                // Failed to query headers
                HTTPRequestComplete();
                return;
            }

            if (HTTP_STATUS_OK != statusCode)
            {
                // Error status
                HTTPRequestComplete();
                return;
            }

            if (!WinHttpReadData(g_Request,
                           g_HTTPBuffer,
                           sizeof(g_HTTPBuffer),
                           0))
            {
                // Error reading data
                HTTPRequestComplete();
                return;
            }
        }
        break;

        case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
        {
            if (dwStatusInformationLength > 0)
            {
                // Store the data

                // Read the next data
                if (!WinHttpReadData(g_Request,
                                     g_HTTPBuffer, 
                                         sizeof(g_HTTPBuffer),
                                     0))
                {
                    // Error
                HTTPRequestComplete();
                return;
            }
        }
        else
        {
            // Request completed OK
            HTTPRequestComplete();
        }
    }
    break;

    default:
        break;
    }
}

// Online functionality
void Online_UpdateServer()
{
    switch(g_OnlineState)
    {
    case OnlineState_Idle:
        {
            // Get our local ip address by connecting a local socket to a web address and reading the socket name

            // Look up the address to connect to
            addrinfo hints;
            addrinfo* res = 0;
            memset(&hints, 0, sizeof hints);
            hints.ai_family = AF_UNSPEC;
            hints.ai_socktype = SOCK_DGRAM;
            getaddrinfo("www.google.com", "80", &hints, &res);

            // Create the socket
            int tempSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
            unsigned int localAddress = 0;
            if (tempSocket >= 0)
            {
                // Connect the socket
                connect(tempSocket, res->ai_addr, res->ai_addrlen);

                // Get the socket name (our local ip address)
                sockaddr_in localName;
                memset(&localName, 0, sizeof(localName));
                int bufferSize = sizeof(localName);
                if(getsockname(tempSocket, (sockaddr*)&localName, &bufferSize) == 0)
                {
                    // Get the IP address
                    localAddress = localName.sin_addr.S_un.S_addr;
                }

                closesocket(tempSocket);
            }

            // Connect
            g_Connection = WinHttpConnect(g_Internet, L"www.google.com", INTERNET_DEFAULT_PORT, 0);

            // Open the request
            std::wstringstream urlString;
            urlString << L"/";
            std::wstring tempString = urlString.str();
            const wchar_t* wurlString = tempString.c_str();
            g_Request = WinHttpOpenRequest(g_Connection, 0, wurlString, 0, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

            // Install the status callback function.
            if(WINHTTP_INVALID_STATUS_CALLBACK == WinHttpSetStatusCallback(g_Request, (WINHTTP_STATUS_CALLBACK)HTTPAsyncCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL))
            {
                OutputDebugString(L"Error");
            }

            // Send the request
            if(!WinHttpSendRequest(g_Request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
            {
                OutputDebugString(L"Error");
            }

            // Log that we're registering
            g_OnlineState = OnlineState_Registering;
        }
        break;

    case OnlineState_Registering:
        {
            // Don't do anything, as we're currently registering
        }
        break;

    default:
        break;
    }
}

g_Internet 是这样初始化的:

// Initialise HTTP
g_Internet = WinHttpOpen(L"Boomba", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);

据我所知,我正在初始化并正确使用 WinHTTP,一切似乎都恢复正常,没有错误。回调被调用了两次,第一次使用 WINHTTP_CALLBACK_STATUS_RESOLVING_NAME,然后使用 WINHTTP_CALLBACK_STATUS_NAME_RESOLVED,然后我遇到访问冲突:

0xC0000005:访问冲突读取位置0x00000014

位置发生了变化,发生了各种各样的事情,所以我认为它看起来可能是线程问题,但我不确定可能是什么问题。有任何想法吗? (我在 Windows 7 64 位上运行)。

【问题讨论】:

  • 哪一行代码?也许堆栈上有什么东西被破坏了?

标签: c++ winhttp


【解决方案1】:
    if (WINHTTP_INVALID_STATUS_CALLBACK == WinHttpSetStatusCallback(
       g_Request, 
       (WINHTTP_STATUS_CALLBACK)HTTPAsyncCallback,        // <=== evil
       WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 
       NULL))

编译器最初抱怨您的函数与所需的函数指针类型不匹配。您通过使用该强制转换关闭编译器来解决问题。但这并没有真正解决问题。现在你给自己买了一个真正的问题,一个损坏的堆栈。很难诊断。

回调函数必须声明为 __stdcall,而不是默认的 __cdecl 调用约定。 Windows 标头为此使用 CALLBACK 宏。修复:

 void CALLBACK HTTPAsyncCallback(/* etc*/)

当然还有移除那个演员表。

【讨论】:

  • 谢谢,这正是问题所在。我从 Microsoft 文档中复制了对 WinHttpSetStatusCallback 的调用,其中已经有演员表,但没有显示他们的回调声明,所以我从来没有看到编译器错误。不知道为什么他们会在文档中使用该演员表。谢谢:)
  • 这可能已经 2 岁了,但这正是我刚刚遇到的问题。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多