HttpOpenRequest() 中的 WinInet 访问冲突

Posted

技术标签:

【中文标题】HttpOpenRequest() 中的 WinInet 访问冲突【英文标题】:WinInet Access Violation in HttpOpenRequest() 【发布时间】:2021-12-25 18:09:12 【问题描述】:

我正在尝试使用 WinInet 将文件上传到 php 页面。我在其中一个功能上遇到访问冲突,但不明白为什么。我已经从示例页面构建了代码。

代码如下:

HINTERNET aInternet=InternetOpen("My-Custom-Agent/1.0",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
HINTERNET aConnect=InternetConnect(aInternet,"www.myserver.com",INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
if (aConnect)

    HINTERNET aRequest=HttpOpenRequest(aConnect, (const char*)"POST","myphppage.php", NULL, NULL, (const char**)"*/*\0",0,1);
    // ^^
    // Exception happens on this line
    // Exception thrown at 0x70C85B7C (ininet.dll) in TestApp.exe:
    // 0xC00000005: Access violation reading location 0x002A2F2A
    //

当我使用InternetOpenURL() 从服务器下载时,一切似乎都很好。它只是不喜欢我在这里某处所做的事情。任何线索我做错了什么?

【问题讨论】:

与您的问题无关,但在 C++ 中,所有文字字符串实际上都是常量字符数组,它衰减为指向常量字符的指针(即const char*)。换句话说,不需要(const char*)"POST" 中的演员表。事实上,每当你觉得需要进行这样的 C 风格转换时,你应该把它当作你做错了什么的标志。 回到错误的 C 样式转换,字符串 "*/*\0" 将衰减为 const char* 类型的指针。绝对不可能将其用作指向指针的指针(您尝试使用强制转换为const char**)。而且很可能是坠机的原因。如果您删除演员表,编译器应该(正确地)抱怨。抛开这些抱怨不是一个好主意。 某个程序员老兄,这就是答案。但雷米(下)也给出了一个可行的答案,但没有具体说明。既然你先回答了,如果你想在下面给出答案,我会给你功劳。 @KiraHoneybee 我的回答怎么没有提供细节? 读取位置 0x002A2F2A - 这是"*/*" 的二进制值。字符串数据解释为指针。错误已经给你强烈的提示 【参考方案1】:

根据HttpOpenRequest() documentation:

[in] lplpszAcceptTypes

指向以空结尾的字符串数组的指针,指示客户端接受的媒体类型。这是一个例子。

PCTSTR rgpszAcceptTypes[] = _T("text/*"), NULL;

未能正确终止使用 NULL 指针的数组将导致崩溃。

您正在传递一个指向单个以 null 结尾的字符串的指针,错误地类型转换为 const char**

lplpszAcceptTypes -> "*/*"

但该函数需要一个指向 数组 的指针,该数组包含指向以 null 结尾的字符串的指针,其中数组中的最后一个元素必须为 NULL 才能终止数组(因为没有指定函数参数数组中的元素个数):

                     -----
lplpszAcceptTypes -> | 0 | -> "*/*"
                     |---|
                     | 1 | -> NULL
                     -----

看到区别了吗?

该函数将您的字符串文字的 content 误解为好像它是一个指针,但事实并非如此,因此导致 AV 崩溃。发生 AV 的地址 0x002A2F2A 与字符串文字内容的字节数完全相同 ("*/*“ = 0x2A 0x2F 0x2A 0x00)。

你需要改用这个:

LPCSTR rgpszAcceptTypes[] = "*/*", NULL;
HINTERNET aRequest = HttpOpenRequest(aConnect, "POST", "myphppage.php", NULL, NULL, rgpszAcceptTypes, 0, 1);

【讨论】:

嗨,我的字符串中的“\0”扩展为空(通过检查字节确认)。这是另一个问题 - 如果不使用调试器,它可以正常工作。 @KiraHoneybee 字符串文字具有由编译器添加的隐式'\0',因此您无需添加自己的。字符串文字"*/*" 是数组'*', '/', '*', '\0'。通过手动添加'\0',您只是将数组扩展到'*', '/', '*', '\0', '\0',这对函数处理数据的方式没有任何影响。影响它的是内存中字符串数据的布局,而你最初传入的是完全错误的布局,因此崩溃

以上是关于HttpOpenRequest() 中的 WinInet 访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

您可以传递给 Wininet 函数 HttpOpenRequest 的最大 URL 长度是多少?

HttpOpenRequest () Wininet c++ e PHP

Windbg里DLL相关下断点

Delphi SOAP 超时?

如何获取窗口原始大小和位置(wsNormal vs wsMaximized)

对友好 URL 的 POST 请求失败